onc_certification_g10_test_kit 6.0.3 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/
|