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
@@ -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
|