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
@@ -129,6 +129,30 @@ module ONCCertificationG10TestKit
|
|
129
129
|
}
|
130
130
|
end
|
131
131
|
|
132
|
+
group from: :smart_discovery_stu2_2 do # rubocop:disable Naming/VariableNumber
|
133
|
+
required_suite_options(G10Options::SMART_2_2_REQUIREMENT)
|
134
|
+
|
135
|
+
test from: 'g10_smart_well_known_capabilities',
|
136
|
+
config: {
|
137
|
+
options: {
|
138
|
+
required_capabilities: [
|
139
|
+
'launch-ehr',
|
140
|
+
'client-confidential-symmetric',
|
141
|
+
'client-confidential-asymmetric',
|
142
|
+
'sso-openid-connect',
|
143
|
+
'context-banner',
|
144
|
+
'context-style',
|
145
|
+
'context-ehr-patient',
|
146
|
+
'permission-offline',
|
147
|
+
'permission-user',
|
148
|
+
'authorize-post',
|
149
|
+
'permission-v2',
|
150
|
+
'permission-v1'
|
151
|
+
]
|
152
|
+
}
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
132
156
|
group from: :smart_ehr_launch do
|
133
157
|
required_suite_options(G10Options::SMART_1_REQUIREMENT)
|
134
158
|
|
@@ -206,6 +230,16 @@ module ONCCertificationG10TestKit
|
|
206
230
|
},
|
207
231
|
required_suite_options: G10Options::US_CORE_6_REQUIREMENT
|
208
232
|
|
233
|
+
test from: :g10_encounter_context,
|
234
|
+
id: :g10_encounter_context_us_core_7, # rubocop:disable Naming/VariableNumber
|
235
|
+
config: {
|
236
|
+
inputs: {
|
237
|
+
encounter_id: { name: :ehr_encounter_id },
|
238
|
+
access_token: { name: :ehr_access_token }
|
239
|
+
}
|
240
|
+
},
|
241
|
+
required_suite_options: G10Options::US_CORE_7_REQUIREMENT
|
242
|
+
|
209
243
|
test do
|
210
244
|
title 'Launch context contains smart_style_url which links to valid JSON'
|
211
245
|
description %(
|
@@ -362,6 +396,182 @@ module ONCCertificationG10TestKit
|
|
362
396
|
},
|
363
397
|
required_suite_options: G10Options::US_CORE_6_REQUIREMENT
|
364
398
|
|
399
|
+
test from: :g10_encounter_context,
|
400
|
+
id: :g10_encounter_context_us_core_7, # rubocop:disable Naming/VariableNumber
|
401
|
+
config: {
|
402
|
+
inputs: {
|
403
|
+
encounter_id: { name: :ehr_encounter_id },
|
404
|
+
access_token: { name: :ehr_access_token }
|
405
|
+
}
|
406
|
+
},
|
407
|
+
required_suite_options: G10Options::US_CORE_7_REQUIREMENT
|
408
|
+
|
409
|
+
test do
|
410
|
+
title 'Launch context contains smart_style_url which links to valid JSON'
|
411
|
+
description %(
|
412
|
+
In order to mimic the style of the SMART host more closely, SMART apps
|
413
|
+
can check for the existence of this launch context parameter and
|
414
|
+
download the JSON file referenced by the URL value.
|
415
|
+
)
|
416
|
+
uses_request :token
|
417
|
+
id :g10_smart_style_url
|
418
|
+
|
419
|
+
run do
|
420
|
+
skip_if request.status != 200, 'No token response received'
|
421
|
+
assert_valid_json response[:body]
|
422
|
+
|
423
|
+
body = JSON.parse(response[:body])
|
424
|
+
|
425
|
+
assert body['smart_style_url'].present?,
|
426
|
+
'Token response did not contain `smart_style_url`'
|
427
|
+
|
428
|
+
get(body['smart_style_url'])
|
429
|
+
|
430
|
+
assert_response_status(200)
|
431
|
+
assert_valid_json(response[:body])
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
test do
|
436
|
+
title 'Launch context contains need_patient_banner'
|
437
|
+
description %(
|
438
|
+
`need_patient_banner` is a boolean value indicating whether the app
|
439
|
+
was launched in a UX context where a patient banner is required (when
|
440
|
+
true) or not required (when false).
|
441
|
+
)
|
442
|
+
uses_request :token
|
443
|
+
id :g10_smart_need_patient_banner
|
444
|
+
|
445
|
+
run do
|
446
|
+
skip_if request.status != 200, 'No token response received'
|
447
|
+
assert_valid_json response[:body]
|
448
|
+
|
449
|
+
body = JSON.parse(response[:body])
|
450
|
+
|
451
|
+
assert body.key?('need_patient_banner'),
|
452
|
+
'Token response did not contain `need_patient_banner`'
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
tests[2].config(
|
457
|
+
outputs: {
|
458
|
+
incorrectly_permitted_tls_versions_messages: {
|
459
|
+
name: :auth_incorrectly_permitted_tls_versions_messages
|
460
|
+
}
|
461
|
+
}
|
462
|
+
)
|
463
|
+
|
464
|
+
tests[5].config(
|
465
|
+
outputs: {
|
466
|
+
incorrectly_permitted_tls_versions_messages: {
|
467
|
+
name: :token_incorrectly_permitted_tls_versions_messages
|
468
|
+
}
|
469
|
+
}
|
470
|
+
)
|
471
|
+
end
|
472
|
+
|
473
|
+
group from: :smart_ehr_launch_stu2_2, # rubocop:disable Naming/VariableNumber
|
474
|
+
config: {
|
475
|
+
inputs: {
|
476
|
+
use_pkce: {
|
477
|
+
default: 'true',
|
478
|
+
locked: true
|
479
|
+
},
|
480
|
+
pkce_code_challenge_method: {
|
481
|
+
locked: true
|
482
|
+
},
|
483
|
+
authorization_method: {
|
484
|
+
name: :ehr_authorization_method,
|
485
|
+
default: 'post',
|
486
|
+
locked: true
|
487
|
+
}
|
488
|
+
}
|
489
|
+
} do
|
490
|
+
required_suite_options(G10Options::SMART_2_2_REQUIREMENT)
|
491
|
+
|
492
|
+
title 'EHR Launch With Practitioner Scope'
|
493
|
+
input :client_secret,
|
494
|
+
name: :ehr_client_secret,
|
495
|
+
title: 'EHR Launch Client Secret',
|
496
|
+
description: 'Client Secret provided during registration of Inferno as an EHR launch application',
|
497
|
+
optional: false
|
498
|
+
|
499
|
+
config(
|
500
|
+
inputs: {
|
501
|
+
requested_scopes: {
|
502
|
+
default: %(
|
503
|
+
launch openid fhirUser offline_access user/Medication.rs
|
504
|
+
user/AllergyIntolerance.rs user/CarePlan.rs user/CareTeam.rs
|
505
|
+
user/Condition.rs user/Device.rs user/DiagnosticReport.rs
|
506
|
+
user/DocumentReference.rs user/Encounter.rs user/Goal.rs
|
507
|
+
user/Immunization.rs user/Location.rs user/MedicationRequest.rs
|
508
|
+
user/Observation.rs user/Organization.rs user/Patient.rs
|
509
|
+
user/Practitioner.rs user/Procedure.rs user/Provenance.rs
|
510
|
+
user/PractitionerRole.rs
|
511
|
+
).gsub(/\s{2,}/, ' ').strip
|
512
|
+
}
|
513
|
+
}
|
514
|
+
)
|
515
|
+
|
516
|
+
test from: :g10_smart_scopes do
|
517
|
+
title 'User-level access with OpenID Connect and Refresh Token scopes used.'
|
518
|
+
config(
|
519
|
+
inputs: {
|
520
|
+
requested_scopes: { name: :ehr_requested_scopes },
|
521
|
+
received_scopes: { name: :ehr_received_scopes }
|
522
|
+
},
|
523
|
+
options: {
|
524
|
+
scope_version: :v22,
|
525
|
+
required_scope_type: 'user',
|
526
|
+
required_scopes: ['openid', 'fhirUser', 'launch', 'offline_access']
|
527
|
+
}
|
528
|
+
)
|
529
|
+
end
|
530
|
+
|
531
|
+
test from: :g10_unauthorized_access,
|
532
|
+
config: {
|
533
|
+
inputs: {
|
534
|
+
patient_id: { name: :ehr_patient_id }
|
535
|
+
}
|
536
|
+
}
|
537
|
+
|
538
|
+
test from: :g10_patient_context,
|
539
|
+
config: {
|
540
|
+
inputs: {
|
541
|
+
patient_id: { name: :ehr_patient_id },
|
542
|
+
access_token: { name: :ehr_access_token }
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
test from: :g10_encounter_context,
|
547
|
+
config: {
|
548
|
+
inputs: {
|
549
|
+
encounter_id: { name: :ehr_encounter_id },
|
550
|
+
access_token: { name: :ehr_access_token }
|
551
|
+
}
|
552
|
+
},
|
553
|
+
required_suite_options: G10Options::US_CORE_5_REQUIREMENT
|
554
|
+
|
555
|
+
test from: :g10_encounter_context,
|
556
|
+
id: :g10_encounter_context_us_core_6, # rubocop:disable Naming/VariableNumber
|
557
|
+
config: {
|
558
|
+
inputs: {
|
559
|
+
encounter_id: { name: :ehr_encounter_id },
|
560
|
+
access_token: { name: :ehr_access_token }
|
561
|
+
}
|
562
|
+
},
|
563
|
+
required_suite_options: G10Options::US_CORE_6_REQUIREMENT
|
564
|
+
|
565
|
+
test from: :g10_encounter_context,
|
566
|
+
id: :g10_encounter_context_us_core_7, # rubocop:disable Naming/VariableNumber
|
567
|
+
config: {
|
568
|
+
inputs: {
|
569
|
+
encounter_id: { name: :ehr_encounter_id },
|
570
|
+
access_token: { name: :ehr_access_token }
|
571
|
+
}
|
572
|
+
},
|
573
|
+
required_suite_options: G10Options::US_CORE_7_REQUIREMENT
|
574
|
+
|
365
575
|
test do
|
366
576
|
title 'Launch context contains smart_style_url which links to valid JSON'
|
367
577
|
description %(
|
@@ -427,6 +637,30 @@ module ONCCertificationG10TestKit
|
|
427
637
|
end
|
428
638
|
|
429
639
|
group from: :smart_openid_connect,
|
640
|
+
required_suite_options: G10Options::SMART_1_REQUIREMENT,
|
641
|
+
config: {
|
642
|
+
inputs: {
|
643
|
+
id_token: { name: :ehr_id_token },
|
644
|
+
client_id: { name: :ehr_client_id },
|
645
|
+
requested_scopes: { name: :ehr_requested_scopes },
|
646
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
647
|
+
}
|
648
|
+
}
|
649
|
+
|
650
|
+
group from: :smart_openid_connect,
|
651
|
+
required_suite_options: G10Options::SMART_2_REQUIREMENT,
|
652
|
+
id: :smart_openid_connect_stu2,
|
653
|
+
config: {
|
654
|
+
inputs: {
|
655
|
+
id_token: { name: :ehr_id_token },
|
656
|
+
client_id: { name: :ehr_client_id },
|
657
|
+
requested_scopes: { name: :ehr_requested_scopes },
|
658
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
659
|
+
}
|
660
|
+
}
|
661
|
+
|
662
|
+
group from: :smart_openid_connect_stu2_2, # rubocop:disable Naming/VariableNumber
|
663
|
+
required_suite_options: G10Options::SMART_2_2_REQUIREMENT,
|
430
664
|
config: {
|
431
665
|
inputs: {
|
432
666
|
id_token: { name: :ehr_id_token },
|
@@ -0,0 +1,188 @@
|
|
1
|
+
module ONCCertificationG10TestKit
|
2
|
+
class SmartFineGrainedScopesGroupSTU22 < USCoreTestKit::USCoreV610::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_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
|
@@ -0,0 +1,188 @@
|
|
1
|
+
module ONCCertificationG10TestKit
|
2
|
+
class SmartFineGrainedScopesUSCore7Group < 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/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
|
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.first
|
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.first
|
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
|