Skip to content

Commit

Permalink
FI-2849: Add token introspection tests (#531)
Browse files Browse the repository at this point in the history
* add token introspection group

* update short id map

* remove introspection attestation

* update procedure metadata

* remove us core 6 from test matrix test list

* update matrix generation to support three levels of nesting

* fix linting error
  • Loading branch information
Jammjammjamm authored Jul 12, 2024
1 parent 71c22e2 commit 30e1153
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 45 deletions.
3 changes: 3 additions & 0 deletions lib/onc_certification_g10_test_kit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
require_relative 'onc_certification_g10_test_kit/multi_patient_api_stu1'
require_relative 'onc_certification_g10_test_kit/multi_patient_api_stu2'
require_relative 'onc_certification_g10_test_kit/terminology_binding_validator'
require_relative 'onc_certification_g10_test_kit/token_introspection_group'
require_relative 'onc_certification_g10_test_kit/token_revocation_group'
require_relative 'onc_certification_g10_test_kit/visual_inspection_and_attestations_group'
require_relative 'inferno/terminology'
Expand Down Expand Up @@ -363,6 +364,8 @@ def self.well_known_route_handler
group from: :g10_ehr_patient_launch_stu2,
required_suite_options: G10Options::SMART_2_REQUIREMENT

group from: :g10_token_introspection

group from: :g10_visual_inspection_and_attestations
end
end
Expand Down
13 changes: 6 additions & 7 deletions lib/onc_certification_g10_test_kit/onc_program_procedure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1198,17 +1198,16 @@ procedure:
id: TOK-INTRO-1
SUT: |
The health IT developer demonstrates the ability of the Health IT
Module to receive and validate a token it has issued.
Module to receive and validate a token it has issued in accordance
with an implementation specification in § 170.215(c).
TLV: |
The tester verifies the ability of the Health IT Module to receive and
validate a token it has issued.
validate a token it has issued in accordance with an implementation
specification in § 170.215(c).
inferno_supported: 'yes'
inferno_notes: |
No standard is required and therefore Inferno cannot do this in
an automated fashion and this is recorded as an attestation
within Inferno.
inferno_tests:
- 9.10.06
- 9.11.2.01 - 9.11.2.02
- 9.11.3.01 - 9.11.3.02
- section: Paragraph (g)(10)(ii) – Supported search operations
steps:
- group: Supported Search Operations for a Single Patient’s Data
Expand Down
49 changes: 48 additions & 1 deletion lib/onc_certification_g10_test_kit/short_id_map.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2103,13 +2103,60 @@ g10_certification-Group06-g10_ehr_patient_launch_stu2-smart_token_response_body:
g10_certification-Group06-g10_ehr_patient_launch_stu2-smart_token_response_headers: 9.9.09
g10_certification-Group06-g10_ehr_patient_launch_stu2-g10_patient_context: 9.9.10
g10_certification-Group06-g10_ehr_patient_launch_stu2-g10_patient_scope: 9.9.11
g10_certification-Group06-g10_token_introspection: '9.11'
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group: 9.11.1
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery_stu2: 9.11.1.1
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery_stu2-well_known_endpoint
: 9.11.1.1.01
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery_stu2-well_known_capabilities_stu2
: 9.11.1.1.02
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2: 9.11.1.2
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2-standalone_auth_tls
: 9.11.1.2.01
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2-smart_app_redirect_stu2
: 9.11.1.2.02
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2-smart_code_received
: 9.11.1.2.03
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2-standalone_token_tls
: 9.11.1.2.04
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2-smart_token_exchange
: 9.11.1.2.05
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2-smart_token_response_body
: 9.11.1.2.06
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch_stu2-smart_token_response_headers
: 9.11.1.2.07
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery: 9.11.1.3
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery-Test01: 9.11.1.3.01
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery-Test02: 9.11.1.3.02
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery-Test03: 9.11.1.3.03
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_discovery-Test04: 9.11.1.3.04
g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch: 9.11.1.4
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch-standalone_auth_tls
: 9.11.1.4.01
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch-smart_app_redirect
: 9.11.1.4.02
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch-smart_code_received
: 9.11.1.4.03
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch-standalone_token_tls
: 9.11.1.4.04
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch-smart_token_exchange
: 9.11.1.4.05
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch-smart_token_response_body
: 9.11.1.4.06
? g10_certification-Group06-g10_token_introspection-smart_token_introspection_access_token_group-smart_standalone_launch-smart_token_response_headers
: 9.11.1.4.07
g10_certification-Group06-g10_token_introspection-smart_token_introspection_request_group: 9.11.2
g10_certification-Group06-g10_token_introspection-smart_token_introspection_request_group-Test01: 9.11.2.01
g10_certification-Group06-g10_token_introspection-smart_token_introspection_request_group-Test02: 9.11.2.02
g10_certification-Group06-g10_token_introspection-smart_token_introspection_response_group: 9.11.3
g10_certification-Group06-g10_token_introspection-smart_token_introspection_response_group-Test01: 9.11.3.01
g10_certification-Group06-g10_token_introspection-smart_token_introspection_response_group-Test02: 9.11.3.02
g10_certification-Group06-g10_visual_inspection_and_attestations: '9.10'
g10_certification-Group06-g10_visual_inspection_and_attestations-Test01: 9.10.01
g10_certification-Group06-g10_visual_inspection_and_attestations-Test02: 9.10.02
g10_certification-Group06-g10_visual_inspection_and_attestations-Test03: 9.10.03
g10_certification-Group06-g10_visual_inspection_and_attestations-Test04: 9.10.04
g10_certification-Group06-g10_visual_inspection_and_attestations-Test05: 9.10.05
g10_certification-Group06-g10_visual_inspection_and_attestations-Test06: 9.10.06
g10_certification-Group06-g10_visual_inspection_and_attestations-Test07: 9.10.07
g10_certification-Group06-g10_visual_inspection_and_attestations-Test08: 9.10.08
g10_certification-Group06-g10_visual_inspection_and_attestations-Test09: 9.10.09
Expand Down
28 changes: 28 additions & 0 deletions lib/onc_certification_g10_test_kit/tasks/generate_matrix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ def generate_matrix_worksheet # rubocop:disable Metrics/CyclomaticComplexity
# full_test_id = "#{test_case.prefix}#{test.id}"
column_map[test.short_id] = col
end

test_case.groups.each do |nested_group|
nested_group.tests.each do |test|
column_map[test.short_id] = col
end
end
col += 1
end
end
Expand Down Expand Up @@ -231,6 +237,8 @@ def generate_inferno_test_worksheet # rubocop:disable Metrics/CyclomaticComplexi
row = 1

test_suite.groups.each do |group|
next if group.short_id == '6' # Skip US Core 5

row += 1
inferno_worksheet.add_cell(row, 0, "#{group.short_id}: #{group.title}")
inferno_worksheet.add_cell(row, 6, applicable_options(group).map(&:value).uniq.join(', '))
Expand All @@ -252,6 +260,26 @@ def generate_inferno_test_worksheet # rubocop:disable Metrics/CyclomaticComplexi
inferno_worksheet.change_row_vertical_alignment(row, 'top')
row += 1
end

test_case.groups.each do |nested_group|
inferno_worksheet.add_cell(row, 1, "#{nested_group.short_id}: #{nested_group.title}")
inferno_worksheet.add_cell(row, 6, applicable_options(nested_group).map(&:value).uniq.join(', '))

row += 1
nested_group.tests.each do |test|
this_row = columns.map do |column|
column[2].call(test)
end

this_row.each_with_index do |value, index|
inferno_worksheet.add_cell(row, index, value).change_text_wrap(true)
end
inferno_worksheet
.change_row_height(row, [26, ((test.description || '').strip.lines.count * 10) + 10].max)
inferno_worksheet.change_row_vertical_alignment(row, 'top')
row += 1
end
end
end
end

Expand Down
115 changes: 115 additions & 0 deletions lib/onc_certification_g10_test_kit/token_introspection_group.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
require 'smart_app_launch/standalone_launch_group'
require 'smart_app_launch/discovery_stu1_group'
require 'smart_app_launch/token_introspection_group'

require_relative 'g10_options'

module ONCCertificationG10TestKit
class TokenIntrospectionGroup < SMARTAppLaunch::SMARTTokenIntrospectionGroup
id :g10_token_introspection

description <<~DESCRIPTION
# Background
OAuth 2.0 Token introspection, as described in
[RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662), allows an
authorized resource server to query an OAuth 2.0 authorization server for
metadata on a token. The [SMART App Launch STU2 Implementation Guide
Section on Token
Introspection](https://hl7.org/fhir/smart-app-launch/STU2/token-introspection.html)
states that
> SMART on FHIR EHRs SHOULD support token introspection, which allows a
> broader ecosystem of resource servers to leverage authorization
> decisions managed by a single authorization server.
# Test Methodology
In these tests, Inferno acts as an authorized resource server that queries
the authorization server about an access token, rather than a client to a
FHIR resource server as in the previous SMART App Launch tests. Ideally,
Inferno should be registered with the authorization server as an
authorized resource server capable of accessing the token introspection
endpoint through client credentials, per the SMART IG recommendations.
However, the SMART IG only formally REQUIRES "some form of authorization"
to access the token introspection endpoint and does not specifiy any one
specific approach. As such, the token introspection tests are broken up
into three groups that each complete a discrete step in the token
introspection process:
1. **Request Access Token Group** - repeats a subset of Standalone Launch
tests in order to receive a new access token with an authorization code
grant.
2. **Issue Token Introspection Request Group** - completes the
introspection requests.
3. **Validate Token Introspection Response Group** - validates the
contents of the introspection responses.
See the individual test groups for more details and guidance.
DESCRIPTION

input_instructions <<~INSTRUCTIONS
If the introspection endpoint is protected, testers must enter their own
HTTP Authorization header for the introspection request. See [RFC 7616 The
'Basic' HTTP Authentication
Scheme](https://datatracker.ietf.org/doc/html/rfc7617) for the most common
approach that uses client credentials. Testers may also provide any
additional parameters needed for their authorization server to complete
the introspection request.
**Note:** For both the Authorization header and request parameters, user-input
values will be sent exactly as entered and therefore the tester must
URI-encode any appropriate values.
INSTRUCTIONS

run_as_group

input :well_known_introspection_url,
title: 'Token Introspection Endpoint',
description: <<~DESCRIPTION,
The complete URL of the token introspection endpoint. This will be
populated automatically if included in the server's discovery
endpoint.
DESCRIPTION
optional: true

input_order :url,
:well_known_introspection_url,
:custom_authorization_header,
:optional_introspection_request_params,
:standalone_client_id,
:standalone_client_secret,
:authorization_method,
:use_pkce,
:pkce_code_challenge_method,
:standalone_requested_scopes,
:client_auth_encryption_method,
:client_auth_type

groups.first.description <<~DESCRIPTION
These tests are perform discovery and a standalone launch in order to
receive a new, active access token that will be provided for token
introspection.
DESCRIPTION

groups[1].description <<~DESCRIPTION
This group of tests executes the token introspection requests and ensures
the correct HTTP response is returned but does not validate the contents
of the token introspection response.
DESCRIPTION

# The token introspection tests are SMART v2 only, so they use v2 discovery
# and launch groups. g10 needs them for SMART v1 and v2, so this sets the
# original discovery and launch groups to only appear when using SMART v2,
# and adds the v1 groups when using v1.

groups.first.groups.each do |group|
group.required_suite_options(G10Options::SMART_2_REQUIREMENT)
end

groups.first.group from: :smart_discovery,
required_suite_options: G10Options::SMART_1_REQUIREMENT

groups.first.group from: :smart_standalone_launch,
required_suite_options: G10Options::SMART_1_REQUIREMENT
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -189,43 +189,6 @@ class VisualInspectionAndAttestationsGroup < Inferno::TestGroup
end
end

test do
title 'Health IT developer demonstrated the ability of the Health IT Module / ' \
'authorization server to validate token it has issued.'
description %(
Health IT developer demonstrated the ability of the Health IT Module /
authorization server to validate token it has issued
)
id 'Test06'
input :token_validation_support,
title: 'Health IT developer demonstrated the ability of the Health IT Module / authorization server to validate token it has issued.', # rubocop:disable Layout/LineLength
type: 'radio',
default: 'false',
options: {
list_options: [
{
label: 'Yes',
value: 'true'
},
{
label: 'No',
value: 'false'
}
]
}
input :token_validation_notes,
title: 'Notes, if applicable:',
type: 'textarea',
optional: true

run do
assert token_validation_support == 'true',
'Health IT Module did not demonstrate the ability of the Health IT Module / ' \
'authorization server to validate token it has issued'
pass token_validation_notes if token_validation_notes.present?
end
end

test do
title 'Tester verifies that all information is accurate and without omission.'
description %(
Expand Down

0 comments on commit 30e1153

Please sign in to comment.