onc_certification_g10_test_kit 6.0.3 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/inferno/repositiories/validators.rb +0 -6
- data/lib/inferno/repositiories/value_sets.rb +1 -7
- data/lib/inferno/terminology/expected_manifest.yml +5 -5
- data/lib/inferno/terminology/fhir_package_manager.rb +13 -4
- data/lib/inferno/terminology/loader.rb +2 -1
- data/lib/inferno/terminology/tasks/download_fhir_terminology.rb +2 -1
- data/lib/inferno/terminology/tasks/download_umls.rb +2 -1
- data/lib/inferno/terminology/tasks/expand_value_set_to_file.rb +1 -1
- data/lib/inferno/terminology/tasks/run_umls_jar.rb +2 -1
- data/lib/inferno/terminology/validator.rb +1 -0
- data/lib/inferno/terminology/value_set.rb +2 -0
- data/lib/onc_certification_g10_test_kit/all_resources.rb +74 -0
- data/lib/onc_certification_g10_test_kit/bulk_data_group_export_validation.rb +361 -59
- data/lib/onc_certification_g10_test_kit/bulk_export_validation_tester.rb +4 -3
- data/lib/onc_certification_g10_test_kit/g10_options.rb +20 -1
- data/lib/onc_certification_g10_test_kit/limited_scope_grant_test.rb +4 -0
- data/lib/onc_certification_g10_test_kit/multi_patient_api_stu1.rb +2 -1
- data/lib/onc_certification_g10_test_kit/multi_patient_api_stu2.rb +2 -1
- data/lib/onc_certification_g10_test_kit/patient_scope_test.rb +1 -1
- data/lib/onc_certification_g10_test_kit/profile_selector.rb +40 -15
- data/lib/onc_certification_g10_test_kit/restricted_resource_type_access_group.rb +89 -2
- data/lib/onc_certification_g10_test_kit/short_id_map.yml +1417 -12
- data/lib/onc_certification_g10_test_kit/single_patient_us_core_7_api_group.rb +219 -0
- data/lib/onc_certification_g10_test_kit/smart_app_launch_invalid_aud_group.rb +41 -1
- data/lib/onc_certification_g10_test_kit/smart_asymmetric_launch_group.rb +33 -1
- data/lib/onc_certification_g10_test_kit/smart_ehr_patient_launch_group_stu2_2.rb +128 -0
- data/lib/onc_certification_g10_test_kit/smart_ehr_practitioner_app_group.rb +234 -0
- data/lib/onc_certification_g10_test_kit/smart_fine_grained_scopes_group_stu2_2.rb +188 -0
- data/lib/onc_certification_g10_test_kit/smart_fine_grained_scopes_us_core_7_group.rb +188 -0
- data/lib/onc_certification_g10_test_kit/smart_fine_grained_scopes_us_core_7_group_stu2_2.rb +188 -0
- data/lib/onc_certification_g10_test_kit/smart_granular_scope_selection_group.rb +67 -1
- data/lib/onc_certification_g10_test_kit/smart_limited_app_group.rb +128 -1
- data/lib/onc_certification_g10_test_kit/smart_public_standalone_launch_group_stu2_2.rb +162 -0
- data/lib/onc_certification_g10_test_kit/smart_scopes_test.rb +10 -2
- data/lib/onc_certification_g10_test_kit/smart_standalone_patient_app_group.rb +159 -0
- data/lib/onc_certification_g10_test_kit/smart_v1_scopes_group.rb +117 -0
- data/lib/onc_certification_g10_test_kit/terminology_binding_validator.rb +5 -1
- data/lib/onc_certification_g10_test_kit/token_introspection_group_stu2_2.rb +97 -0
- data/lib/onc_certification_g10_test_kit/unrestricted_resource_type_access_group.rb +85 -31
- data/lib/onc_certification_g10_test_kit/version.rb +1 -1
- data/lib/onc_certification_g10_test_kit/visual_inspection_and_attestations_group.rb +171 -0
- data/lib/onc_certification_g10_test_kit/well_known_capabilities_test.rb +1 -1
- data/lib/onc_certification_g10_test_kit.rb +72 -5
- metadata +18 -10
@@ -0,0 +1,188 @@
|
|
1
|
+
module ONCCertificationG10TestKit
|
2
|
+
class SmartFineGrainedScopesUSCore7GroupSTU22 < USCoreTestKit::USCoreV700::SmartGranularScopesGroup
|
3
|
+
title 'SMART App Launch with fine-grained scopes'
|
4
|
+
short_title 'SMART Launch with Fine-Grained Scopes'
|
5
|
+
|
6
|
+
input_instructions %(
|
7
|
+
If necessary, register Inferno as a standalone application using the following information:
|
8
|
+
|
9
|
+
* Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
|
10
|
+
|
11
|
+
Inferno may be registered multiple times with different `client_ids`, or this
|
12
|
+
may reuse a single registration of Inferno.`
|
13
|
+
|
14
|
+
This test will perform two launches, with each launch requiring a separate
|
15
|
+
separate set of finer-grained scopes to be granted:
|
16
|
+
|
17
|
+
Group 1:
|
18
|
+
* `Condition.rs?category=http://terminology.hl7.org/CodeSystem/condition-category|encounter-diagnosis`
|
19
|
+
* `Condition.rs?category=http://hl7.org/fhir/us/core/CodeSystem/condition-category|health-concern`
|
20
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory`
|
21
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|social-history`
|
22
|
+
|
23
|
+
Group 2:
|
24
|
+
* `Condition.rs?category=http://terminology.hl7.org/CodeSystem/condition-category|problem-list-item`
|
25
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs`
|
26
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|survey`
|
27
|
+
* `Observation.rs?category=http://hl7.org/fhir/us/core/CodeSystem/us-core-category|sdoh`
|
28
|
+
)
|
29
|
+
|
30
|
+
description <<~DESCRIPTION
|
31
|
+
|
32
|
+
As finalized in the [HTI-1 Final Rule](https://www.federalregister.gov/d/2023-28857/p-1250), Health IT Modules are
|
33
|
+
required to support SMART App Launch v2.0.0 "Finer-grained resource
|
34
|
+
constraints using search parameters" for the "category" parameter for the
|
35
|
+
Condition resource with Condition sub-resources Encounter Diagnosis, Problem
|
36
|
+
List, and Health Concern, and the Observation resource with Observation
|
37
|
+
sub-resources Clinical Test, Laboratory, Social History, SDOH, Survey, and
|
38
|
+
Vital Signs.
|
39
|
+
|
40
|
+
This is also reflected in the (g)(10) Standardized API for patient and
|
41
|
+
populations [Test
|
42
|
+
Procedure](https://www.healthit.gov/test-method/standardized-api-patient-and-population-services#test_procedure):
|
43
|
+
|
44
|
+
> [AUT-PAT-28] SMART v2 scope syntax for patient-level and user-level scopes to support
|
45
|
+
the “permission-v2” “SMART on FHIR® Capability”, including support for
|
46
|
+
finer-grained resource constraints using search parameters according to
|
47
|
+
section 3.0.2.3 of the implementation specification at § 170.215(c)(2) for
|
48
|
+
the “category” parameter for the following resources: (1) Condition
|
49
|
+
resource with Condition sub-resources Encounter Diagnosis, Problem List,
|
50
|
+
and Health Concern; and (2) Observation resource with Observation
|
51
|
+
sub-resources Clinical Test, Laboratory, Social History, SDOH, Survey, and
|
52
|
+
Vital Signs
|
53
|
+
|
54
|
+
Prior to running this scenario, first run the Single Patient API tests using
|
55
|
+
resource-level scopes, as this scenario uses content saved from that scenario
|
56
|
+
as a baseline for comparison when finer-grained scopes are granted.
|
57
|
+
|
58
|
+
This scenario contains two groups of finer-grained scope tests, each of
|
59
|
+
which includes a SMART Standalone Launch that requests a subset of
|
60
|
+
finer-grained scopes, followed by FHIR API requests to verify that scopes
|
61
|
+
are appropriately granted. The app launches require that the subset of the
|
62
|
+
requested finer-grained scopes are granted by the user. The FHIR API tests then repeat all
|
63
|
+
of the queries from the original Single Patient API tests that were run
|
64
|
+
using resource-level scopes, and verify that only resources matching the
|
65
|
+
current finer-grained scopes are returned. Each group requires a separate
|
66
|
+
set of finer-grained scopes to be granted:
|
67
|
+
|
68
|
+
Group 1:
|
69
|
+
* `Condition.rs?category=http://terminology.hl7.org/CodeSystem/condition-category|encounter-diagnosis`
|
70
|
+
* `Condition.rs?category=http://hl7.org/fhir/us/core/CodeSystem/condition-category|health-concern`
|
71
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory`
|
72
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|social-history`
|
73
|
+
|
74
|
+
Group 2:
|
75
|
+
* `Condition.rs?category=http://terminology.hl7.org/CodeSystem/condition-category|problem-list-item`
|
76
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs`
|
77
|
+
* `Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|survey`
|
78
|
+
* `Observation.rs?category=http://hl7.org/fhir/us/core/CodeSystem/us-core-category|sdoh`
|
79
|
+
|
80
|
+
Note that Inferno will only request the finer grained scopes in each case,
|
81
|
+
but the system under test can display more scopes to the tester during
|
82
|
+
authorization. In this case, it is expected that the tester will only
|
83
|
+
approve the appropriate scopes in each group as described above.
|
84
|
+
|
85
|
+
For more information, please refer to [finer-grained resource constraints
|
86
|
+
using search
|
87
|
+
parameters](https://hl7.org/fhir/smart-app-launch/STU2.2/scopes-and-launch-context.html#finer-grained-resource-constraints-using-search-parameters).
|
88
|
+
|
89
|
+
DESCRIPTION
|
90
|
+
|
91
|
+
id :g10_us_core_7_smart_fine_grained_scopes_stu2_2 # rubocop:disable Naming/VariableNumber
|
92
|
+
|
93
|
+
input :url
|
94
|
+
|
95
|
+
children.each(&:run_as_group)
|
96
|
+
|
97
|
+
# Replace generic finer-grained scope auth group with which allows standalone or
|
98
|
+
# ehr launch with just the standalone launch group
|
99
|
+
granular_scopes_group1 = children.first
|
100
|
+
granular_scopes_group1.children[0] = granular_scopes_group1.children.first.children[1]
|
101
|
+
standalone_launch_group1 = granular_scopes_group1.children[0]
|
102
|
+
standalone_launch_group1.required
|
103
|
+
|
104
|
+
granular_scopes_group2 = children.last
|
105
|
+
granular_scopes_group2.children[0] = granular_scopes_group2.children.first.children[1]
|
106
|
+
standalone_launch_group2 = granular_scopes_group2.children[0]
|
107
|
+
standalone_launch_group2.required
|
108
|
+
|
109
|
+
# Move the granular scope API groups to the top level
|
110
|
+
api_group1 = granular_scopes_group1.children.pop
|
111
|
+
api_group1.children.each do |group|
|
112
|
+
group.children.select!(&:required?)
|
113
|
+
granular_scopes_group1.children << group
|
114
|
+
end
|
115
|
+
|
116
|
+
api_group2 = granular_scopes_group2.children.pop
|
117
|
+
api_group2.children.each do |group|
|
118
|
+
group.children.select!(&:required?)
|
119
|
+
granular_scopes_group2.children << group
|
120
|
+
end
|
121
|
+
|
122
|
+
# Remove OIDC and refresh token tests
|
123
|
+
standalone_launch_group1.children.pop(2)
|
124
|
+
standalone_launch_group2.children.pop(2)
|
125
|
+
|
126
|
+
config(
|
127
|
+
inputs: {
|
128
|
+
authorization_method: {
|
129
|
+
name: :granular_scopes_authorization_method,
|
130
|
+
title: 'Granular Scopes Authorization Request Method'
|
131
|
+
},
|
132
|
+
client_auth_type: {
|
133
|
+
name: :granular_scopes_client_auth_type,
|
134
|
+
title: 'Granular Scopes Client Authentication Type'
|
135
|
+
},
|
136
|
+
received_scopes: {
|
137
|
+
name: :standalone_received_scopes
|
138
|
+
}
|
139
|
+
}
|
140
|
+
)
|
141
|
+
|
142
|
+
granular_scopes_group1.config(
|
143
|
+
inputs: {
|
144
|
+
client_id: {
|
145
|
+
name: :granular_scopes1_client_id,
|
146
|
+
title: 'Granular Scopes Group 1 Client ID'
|
147
|
+
},
|
148
|
+
client_secret: {
|
149
|
+
name: :granular_scopes1_client_secret,
|
150
|
+
title: 'Granular Scopes Group 1 Client Secret'
|
151
|
+
},
|
152
|
+
requested_scopes: {
|
153
|
+
title: 'Granular Scopes Group 1 Scopes'
|
154
|
+
}
|
155
|
+
}
|
156
|
+
)
|
157
|
+
|
158
|
+
granular_scopes_group2.config(
|
159
|
+
inputs: {
|
160
|
+
client_id: {
|
161
|
+
name: :granular_scopes2_client_id,
|
162
|
+
title: 'Granular Scopes Group 2 Client ID'
|
163
|
+
},
|
164
|
+
client_secret: {
|
165
|
+
name: :granular_scopes2_client_secret,
|
166
|
+
title: 'Granular Scopes Group 2 Client Secret'
|
167
|
+
},
|
168
|
+
requested_scopes: {
|
169
|
+
title: 'Granular Scopes Group 2 Scopes'
|
170
|
+
}
|
171
|
+
}
|
172
|
+
)
|
173
|
+
|
174
|
+
input_order :url,
|
175
|
+
:granular_scopes1_client_id,
|
176
|
+
:requested_scopes_group1,
|
177
|
+
:granular_scopes_authorization_method,
|
178
|
+
:granular_scopes_client_auth_type,
|
179
|
+
:granular_scopes1_client_secret,
|
180
|
+
:client_auth_encryption_method,
|
181
|
+
:granular_scopes2_client_id,
|
182
|
+
:requested_scopes_group2,
|
183
|
+
:granular_scopes2_client_secret,
|
184
|
+
:use_pkce,
|
185
|
+
:pkce_code_challenge_method,
|
186
|
+
:patient_ids
|
187
|
+
end
|
188
|
+
end
|
@@ -84,9 +84,13 @@ module ONCCertificationG10TestKit
|
|
84
84
|
}
|
85
85
|
)
|
86
86
|
|
87
|
-
group from: :smart_discovery_stu2
|
87
|
+
group from: :smart_discovery_stu2,
|
88
|
+
required_suite_options: G10Options::SMART_2_REQUIREMENT
|
89
|
+
group from: :smart_discovery_stu2_2, # rubocop:disable Naming/VariableNumber
|
90
|
+
required_suite_options: G10Options::SMART_2_2_REQUIREMENT
|
88
91
|
|
89
92
|
group from: :smart_standalone_launch_stu2 do
|
93
|
+
required_suite_options(G10Options::SMART_2_REQUIREMENT)
|
90
94
|
id :g10_granular_scope_selection_v2_scopes
|
91
95
|
title 'Granular Scope Selection with v2 Scopes'
|
92
96
|
|
@@ -145,6 +149,68 @@ module ONCCertificationG10TestKit
|
|
145
149
|
end
|
146
150
|
end
|
147
151
|
|
152
|
+
test from: :g10_smart_granular_scope_selection
|
153
|
+
end
|
154
|
+
group from: :smart_standalone_launch_stu2_2 do # rubocop:disable Naming/VariableNumber
|
155
|
+
required_suite_options(G10Options::SMART_2_2_REQUIREMENT)
|
156
|
+
id :g10_granular_scope_selection_v2_2_scopes
|
157
|
+
title 'Granular Scope Selection with v2 Scopes'
|
158
|
+
|
159
|
+
config(
|
160
|
+
inputs: {
|
161
|
+
client_id: {
|
162
|
+
name: :granular_scope_selection_v2_client_id,
|
163
|
+
title: 'Granular Scope Selection w/v2 Scopes Client ID'
|
164
|
+
},
|
165
|
+
client_secret: {
|
166
|
+
name: :granular_scope_selection_v2_client_secret,
|
167
|
+
title: 'Granular Scope Selection w/v2 Scopes Client Secret',
|
168
|
+
default: nil,
|
169
|
+
optional: true
|
170
|
+
},
|
171
|
+
requested_scopes: {
|
172
|
+
name: :granular_scope_selection_v2_requested_scopes,
|
173
|
+
title: 'Granular Scope Selection v2 Scopes',
|
174
|
+
default: %(
|
175
|
+
launch/patient openid fhirUser offline_access patient/Condition.rs
|
176
|
+
patient/Observation.rs patient/Patient.rs
|
177
|
+
).gsub(/\s{2,}/, ' ').strip
|
178
|
+
},
|
179
|
+
received_scopes: { name: :granular_scope_selection_v2_received_scopes }
|
180
|
+
},
|
181
|
+
outputs: {
|
182
|
+
requested_scopes: { name: :granular_scope_selection_v2_requested_scopes },
|
183
|
+
received_scopes: { name: :granular_scope_selection_v2_received_scopes }
|
184
|
+
},
|
185
|
+
options: {
|
186
|
+
redirect_message_proc: proc do |auth_url|
|
187
|
+
%(
|
188
|
+
### #{self.class.parent&.parent&.title}
|
189
|
+
|
190
|
+
[Follow this link to authorize with the SMART server](#{auth_url}).
|
191
|
+
|
192
|
+
Tests will resume once Inferno receives a request at
|
193
|
+
`#{config.options[:redirect_uri]}` with a state of `#{state}`.
|
194
|
+
)
|
195
|
+
end,
|
196
|
+
ignore_missing_scopes_check: true
|
197
|
+
}
|
198
|
+
)
|
199
|
+
|
200
|
+
test from: :g10_smart_scopes do
|
201
|
+
config(
|
202
|
+
options: {
|
203
|
+
scope_version: :v22,
|
204
|
+
required_scope_type: 'patient',
|
205
|
+
required_scopes: ['openid', 'fhirUser', 'launch/patient', 'offline_access']
|
206
|
+
}
|
207
|
+
)
|
208
|
+
|
209
|
+
def patient_compartment_resource_types
|
210
|
+
['Patient', 'Condition', 'Observation']
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
148
214
|
test from: :g10_smart_granular_scope_selection
|
149
215
|
end
|
150
216
|
end
|
@@ -210,7 +210,134 @@ module ONCCertificationG10TestKit
|
|
210
210
|
Sequence](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
|
211
211
|
)
|
212
212
|
|
213
|
-
required_suite_options
|
213
|
+
required_suite_options(G10Options::SMART_2_REQUIREMENT)
|
214
|
+
|
215
|
+
config(
|
216
|
+
inputs: {
|
217
|
+
client_id: { locked: true },
|
218
|
+
client_secret: { locked: true },
|
219
|
+
url: { locked: true },
|
220
|
+
requested_scopes: { locked: true },
|
221
|
+
code: { name: :limited_code },
|
222
|
+
state: { name: :limited_state },
|
223
|
+
patient_id: { name: :limited_patient_id },
|
224
|
+
access_token: { name: :limited_access_token },
|
225
|
+
# TODO: separate standalone/ehr discovery outputs
|
226
|
+
smart_authorization_url: { locked: true, title: 'SMART Authorization Url' },
|
227
|
+
smart_token_url: { locked: true, title: 'SMART Token Url' },
|
228
|
+
received_scopes: { name: :limited_received_scopes },
|
229
|
+
smart_credentials: { name: :limited_smart_credentials },
|
230
|
+
client_auth_type: {
|
231
|
+
locked: true,
|
232
|
+
default: 'confidential_symmetric'
|
233
|
+
}
|
234
|
+
},
|
235
|
+
outputs: {
|
236
|
+
code: { name: :limited_code },
|
237
|
+
token_retrieval_time: { name: :limited_token_retrieval_time },
|
238
|
+
state: { name: :limited_state },
|
239
|
+
id_token: { name: :limited_id_token },
|
240
|
+
refresh_token: { name: :limited_refresh_token },
|
241
|
+
access_token: { name: :limited_access_token },
|
242
|
+
expires_in: { name: :limited_expires_in },
|
243
|
+
patient_id: { name: :limited_patient_id },
|
244
|
+
encounter_id: { name: :limited_encounter_id },
|
245
|
+
received_scopes: { name: :limited_received_scopes },
|
246
|
+
intent: { name: :limited_intent },
|
247
|
+
smart_credentials: { name: :limited_smart_credentials }
|
248
|
+
},
|
249
|
+
requests: {
|
250
|
+
redirect: { name: :limited_redirect },
|
251
|
+
token: { name: :limited_token }
|
252
|
+
},
|
253
|
+
options: {
|
254
|
+
ignore_missing_scopes_check: true,
|
255
|
+
redirect_message_proc: lambda do |auth_url|
|
256
|
+
expected_resource_string =
|
257
|
+
expected_resources
|
258
|
+
.split(',')
|
259
|
+
.map(&:strip)
|
260
|
+
.map { |resource_type| "* #{resource_type}\n" }
|
261
|
+
.join
|
262
|
+
|
263
|
+
<<~MESSAGE
|
264
|
+
### #{self.class.parent.parent.title}
|
265
|
+
|
266
|
+
[Follow this link to authorize with the SMART
|
267
|
+
server](#{auth_url}).
|
268
|
+
|
269
|
+
Tests will resume once Inferno receives a request at
|
270
|
+
`#{config.options[:redirect_uri]}` with a state of `#{state}`.
|
271
|
+
|
272
|
+
Access should only be granted to the following resources:
|
273
|
+
|
274
|
+
#{expected_resource_string}
|
275
|
+
MESSAGE
|
276
|
+
end
|
277
|
+
}
|
278
|
+
)
|
279
|
+
|
280
|
+
input :expected_resources,
|
281
|
+
title: 'Expected Resource Grant for Limited Access Launch',
|
282
|
+
description: 'The user will only grant access to the following resources during authorization.',
|
283
|
+
default: 'Patient, Condition, Observation'
|
284
|
+
|
285
|
+
test from: :g10_patient_context,
|
286
|
+
config: {
|
287
|
+
inputs: {
|
288
|
+
patient_id: { name: :limited_patient_id },
|
289
|
+
smart_credentials: { name: :limited_smart_credentials }
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
test from: :g10_limited_scope_grant do
|
294
|
+
config(
|
295
|
+
inputs: {
|
296
|
+
received_scopes: { name: :limited_received_scopes }
|
297
|
+
}
|
298
|
+
)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
group from: :smart_standalone_launch_stu2_2, # rubocop:disable Naming/VariableNumber
|
303
|
+
config: {
|
304
|
+
inputs: {
|
305
|
+
use_pkce: {
|
306
|
+
default: 'true',
|
307
|
+
locked: true
|
308
|
+
},
|
309
|
+
pkce_code_challenge_method: {
|
310
|
+
locked: true
|
311
|
+
}
|
312
|
+
}
|
313
|
+
} do
|
314
|
+
title 'Standalone Launch With Limited Scope'
|
315
|
+
description %(
|
316
|
+
# Background
|
317
|
+
|
318
|
+
The [Standalone
|
319
|
+
Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-standalone-launch)
|
320
|
+
allows an app, like Inferno, to be launched independent of an
|
321
|
+
existing EHR session. It is one of the two launch methods described in
|
322
|
+
the SMART App Launch Framework alongside EHR Launch. The app will
|
323
|
+
request authorization for the provided scope from the authorization
|
324
|
+
endpoint, ultimately receiving an authorization token which can be used
|
325
|
+
to gain access to resources on the FHIR server.
|
326
|
+
|
327
|
+
# Test Methodology
|
328
|
+
|
329
|
+
Inferno will redirect the user to the the authorization endpoint so that
|
330
|
+
they may provide any required credentials and authorize the application.
|
331
|
+
Upon successful authorization, Inferno will exchange the authorization
|
332
|
+
code provided for an access token.
|
333
|
+
|
334
|
+
For more information on the #{title}:
|
335
|
+
|
336
|
+
* [Standalone Launch
|
337
|
+
Sequence](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-standalone-launch)
|
338
|
+
)
|
339
|
+
|
340
|
+
required_suite_options(G10Options::SMART_2_2_REQUIREMENT)
|
214
341
|
|
215
342
|
config(
|
216
343
|
inputs: {
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module ONCCertificationG10TestKit
|
2
|
+
class SMARTPublicStandaloneLaunchGroupTestSTU22 < SMARTAppLaunch::StandaloneLaunchGroupSTU2
|
3
|
+
title 'Public Client Standalone Launch with OpenID Connect'
|
4
|
+
short_title 'Public Client Launch'
|
5
|
+
input_instructions %(
|
6
|
+
Register Inferno as a standalone application using the following information:
|
7
|
+
|
8
|
+
* Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
|
9
|
+
|
10
|
+
Enter in the appropriate scope to enable patient-level access to all
|
11
|
+
relevant resources. In addition, support for the OpenID Connect (openid
|
12
|
+
fhirUser), refresh tokens (offline_access), and patient context
|
13
|
+
(launch/patient) are required.
|
14
|
+
)
|
15
|
+
description %(
|
16
|
+
|
17
|
+
This scenario verifies the ability of systems to support public clients
|
18
|
+
as described in the SMART App Launch implementation specification. Previous
|
19
|
+
scenarios have not required the system under test to demonstrate this
|
20
|
+
specific type of SMART App Launch client.
|
21
|
+
|
22
|
+
Prior to executing this test, register Inferno as a public standalone
|
23
|
+
application using the following information:
|
24
|
+
|
25
|
+
* Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
|
26
|
+
|
27
|
+
Inferno will act as a public client redirect the tester to the the
|
28
|
+
authorization endpoint so that they may provide any required credentials
|
29
|
+
and authorize the application. Upon successful authorization, Inferno will
|
30
|
+
exchange the authorization code provided for an access token.
|
31
|
+
|
32
|
+
For more information on the #{title}:
|
33
|
+
|
34
|
+
* [Standalone Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2.2/index.html#standalone-launch-sequence)
|
35
|
+
)
|
36
|
+
id :g10_public_standalone_launch_stu2_2 # rubocop:disable Naming/VariableNumber
|
37
|
+
run_as_group
|
38
|
+
|
39
|
+
config(
|
40
|
+
inputs: {
|
41
|
+
client_id: {
|
42
|
+
name: :public_client_id,
|
43
|
+
title: 'Public Launch Client ID'
|
44
|
+
},
|
45
|
+
client_secret: {
|
46
|
+
name: :public_client_secret,
|
47
|
+
title: 'Public Launch Client Secret',
|
48
|
+
default: nil,
|
49
|
+
optional: true,
|
50
|
+
locked: true
|
51
|
+
},
|
52
|
+
requested_scopes: {
|
53
|
+
name: :public_requested_scopes,
|
54
|
+
title: 'Public Launch Scope',
|
55
|
+
default: %(
|
56
|
+
launch/patient openid fhirUser offline_access patient/Medication.rs
|
57
|
+
patient/AllergyIntolerance.rs patient/CarePlan.rs
|
58
|
+
patient/CareTeam.rs patient/Condition.rs patient/Device.rs
|
59
|
+
patient/DiagnosticReport.rs patient/DocumentReference.rs
|
60
|
+
patient/Encounter.rs patient/Goal.rs patient/Immunization.rs
|
61
|
+
patient/Location.rs patient/MedicationRequest.rs
|
62
|
+
patient/Observation.rs patient/Organization.rs patient/Patient.rs
|
63
|
+
patient/Practitioner.rs patient/Procedure.rs patient/Provenance.rs
|
64
|
+
patient/PractitionerRole.rs
|
65
|
+
).gsub(/\s{2,}/, ' ').strip
|
66
|
+
},
|
67
|
+
url: {
|
68
|
+
title: 'Public Launch FHIR Endpoint',
|
69
|
+
description: 'URL of the FHIR endpoint used by standalone applications'
|
70
|
+
},
|
71
|
+
code: {
|
72
|
+
name: :public_code
|
73
|
+
},
|
74
|
+
state: {
|
75
|
+
name: :public_state
|
76
|
+
},
|
77
|
+
smart_authorization_url: {
|
78
|
+
title: 'OAuth 2.0 Authorize Endpoint',
|
79
|
+
description: 'OAuth 2.0 Authorize Endpoint provided during the patient standalone launch'
|
80
|
+
},
|
81
|
+
smart_token_url: {
|
82
|
+
title: 'OAuth 2.0 Token Endpoint',
|
83
|
+
description: 'OAuth 2.0 Token Endpoint provided during the patient standalone launch'
|
84
|
+
},
|
85
|
+
smart_credentials: {
|
86
|
+
name: :public_smart_credentials
|
87
|
+
},
|
88
|
+
use_pkce: {
|
89
|
+
default: 'true',
|
90
|
+
locked: true
|
91
|
+
},
|
92
|
+
pkce_code_challenge_method: {
|
93
|
+
locked: true
|
94
|
+
},
|
95
|
+
client_auth_type: {
|
96
|
+
name: :public_client_auth_type,
|
97
|
+
locked: true,
|
98
|
+
default: 'public'
|
99
|
+
}
|
100
|
+
},
|
101
|
+
outputs: {
|
102
|
+
code: { name: :public_code },
|
103
|
+
token_retrieval_time: { name: :public_token_retrieval_time },
|
104
|
+
state: { name: :public_state },
|
105
|
+
id_token: { name: :public_id_token },
|
106
|
+
refresh_token: { name: :public_refresh_token },
|
107
|
+
access_token: { name: :public_access_token },
|
108
|
+
expires_in: { name: :public_expires_in },
|
109
|
+
patient_id: { name: :public_patient_id },
|
110
|
+
encounter_id: { name: :public_encounter_id },
|
111
|
+
received_scopes: { name: :public_received_scopes },
|
112
|
+
intent: { name: :public_intent },
|
113
|
+
smart_credentials: { name: :public_smart_credentials }
|
114
|
+
},
|
115
|
+
requests: {
|
116
|
+
redirect: { name: :public_redirect },
|
117
|
+
token: { name: :public_token }
|
118
|
+
}
|
119
|
+
)
|
120
|
+
|
121
|
+
input_order :url,
|
122
|
+
:public_client_id,
|
123
|
+
:public_client_secret,
|
124
|
+
:public_requested_scopes,
|
125
|
+
:use_pkce,
|
126
|
+
:pkce_code_challenge_method,
|
127
|
+
:smart_authorization_url,
|
128
|
+
:smart_token_url,
|
129
|
+
:authorization_method,
|
130
|
+
:public_client_auth_type
|
131
|
+
|
132
|
+
test from: :g10_patient_context,
|
133
|
+
config: {
|
134
|
+
inputs: {
|
135
|
+
patient_id: { name: :public_patient_id },
|
136
|
+
smart_credentials: { name: :public_smart_credentials }
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
test do
|
141
|
+
title 'OAuth token exchange response contains OpenID Connect id_token'
|
142
|
+
description %(
|
143
|
+
This test requires that an OpenID Connect id_token is provided to
|
144
|
+
demonstrate authentication capabilies for public clients.
|
145
|
+
)
|
146
|
+
id :g10_public_launch_id_token
|
147
|
+
|
148
|
+
input :id_token,
|
149
|
+
name: :public_id_token,
|
150
|
+
locked: true,
|
151
|
+
optional: true
|
152
|
+
|
153
|
+
run do
|
154
|
+
assert id_token.present?, 'Token response did not provide an id_token as required.'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
children.each do |child|
|
159
|
+
child.inputs.delete(:client_auth_encryption_method)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -46,6 +46,8 @@ module ONCCertificationG10TestKit
|
|
46
46
|
V6_VALID_RESOURCE_TYPES =
|
47
47
|
(V5_VALID_RESOURCE_TYPES + ['Coverage', 'MedicationDispense', 'RelatedPerson', 'Specimen']).freeze
|
48
48
|
|
49
|
+
V7_VALID_RESOURCE_TYPES = (V6_VALID_RESOURCE_TYPES + ['Location'])
|
50
|
+
|
49
51
|
PATIENT_COMPARTMENT_RESOURCE_TYPES = [
|
50
52
|
'*',
|
51
53
|
'Patient',
|
@@ -69,6 +71,8 @@ module ONCCertificationG10TestKit
|
|
69
71
|
V6_PATIENT_COMPARTMENT_RESOURCE_TYPES =
|
70
72
|
(V5_PATIENT_COMPARTMENT_RESOURCE_TYPES + ['Coverage', 'MedicationDispense', 'Specimen']).freeze
|
71
73
|
|
74
|
+
V7_PATIENT_COMPARTMENT_RESOURCE_TYPES = (V6_PATIENT_COMPARTMENT_RESOURCE_TYPES + ['Location']).freeze
|
75
|
+
|
72
76
|
attr_accessor :received_or_requested
|
73
77
|
|
74
78
|
def patient_compartment_resource_types
|
@@ -76,6 +80,8 @@ module ONCCertificationG10TestKit
|
|
76
80
|
|
77
81
|
return V6_PATIENT_COMPARTMENT_RESOURCE_TYPES if using_us_core_6?
|
78
82
|
|
83
|
+
return V7_PATIENT_COMPARTMENT_RESOURCE_TYPES if using_us_core_7?
|
84
|
+
|
79
85
|
PATIENT_COMPARTMENT_RESOURCE_TYPES
|
80
86
|
end
|
81
87
|
|
@@ -84,6 +90,8 @@ module ONCCertificationG10TestKit
|
|
84
90
|
|
85
91
|
return V6_VALID_RESOURCE_TYPES if using_us_core_6?
|
86
92
|
|
93
|
+
return V7_VALID_RESOURCE_TYPES if using_us_core_7?
|
94
|
+
|
87
95
|
VALID_RESOURCE_TYPES
|
88
96
|
end
|
89
97
|
|
@@ -117,7 +125,7 @@ module ONCCertificationG10TestKit
|
|
117
125
|
case scope_version
|
118
126
|
when :v1
|
119
127
|
"#{v1_read_format} | *"
|
120
|
-
when :v2
|
128
|
+
when :v2, :v22
|
121
129
|
"#{v2_read_format} | *"
|
122
130
|
else
|
123
131
|
[v1_read_format, v2_read_format, '*'].join(' | ')
|
@@ -128,7 +136,7 @@ module ONCCertificationG10TestKit
|
|
128
136
|
case scope_version
|
129
137
|
when :v1
|
130
138
|
/\A(\*|read)\b/
|
131
|
-
when :v2
|
139
|
+
when :v2, :v22
|
132
140
|
/\A(\*|c?ru?d?s?)\b/
|
133
141
|
else
|
134
142
|
/\A(\*|read|c?ru?d?s?)\b/
|