smart_app_launch_test_kit 0.4.3 → 0.4.4

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.
@@ -5,66 +5,66 @@ module SMARTAppLaunch
5
5
 
6
6
  # All resource types from DSTU3, STU3, R4, R4B, and R5
7
7
  FHIR_RESOURCE_TYPES = [
8
- "Account", "ActivityDefinition", "ActorDefinition",
9
- "AdministrableProductDefinition", "AdverseEvent", "AllergyIntolerance",
10
- "Appointment", "AppointmentResponse", "ArtifactAssessment", "AuditEvent",
11
- "Basic", "Binary", "BiologicallyDerivedProduct",
12
- "BiologicallyDerivedProductDispense", "BodySite", "BodyStructure",
13
- "Bundle", "CapabilityStatement", "CarePlan", "CareTeam", "CatalogEntry",
14
- "ChargeItem", "ChargeItemDefinition", "Citation", "Claim",
15
- "ClaimResponse", "ClinicalImpression", "ClinicalUseDefinition",
16
- "CodeSystem", "Communication", "CommunicationRequest",
17
- "CompartmentDefinition", "Composition", "ConceptMap", "Condition",
18
- "ConditionDefinition", "Conformance", "Consent", "Contract", "Coverage",
19
- "CoverageEligibilityRequest", "CoverageEligibilityResponse",
20
- "DataElement", "DetectedIssue", "Device", "DeviceAssociation",
21
- "DeviceComponent", "DeviceDefinition", "DeviceDispense", "DeviceMetric",
22
- "DeviceRequest", "DeviceUsage", "DeviceUseRequest", "DeviceUseStatement",
23
- "DiagnosticOrder", "DiagnosticReport", "DocumentManifest",
24
- "DocumentReference", "EffectEvidenceSynthesis", "EligibilityRequest",
25
- "EligibilityResponse", "Encounter", "EncounterHistory", "Endpoint",
26
- "EnrollmentRequest", "EnrollmentResponse", "EpisodeOfCare",
27
- "EventDefinition", "Evidence", "EvidenceReport", "EvidenceVariable",
28
- "ExampleScenario", "ExpansionProfile", "ExplanationOfBenefit",
29
- "FamilyMemberHistory", "Flag", "FormularyItem", "GenomicStudy", "Goal",
30
- "GraphDefinition", "Group", "GuidanceResponse", "HealthcareService",
31
- "ImagingManifest", "ImagingObjectSelection", "ImagingSelection",
32
- "ImagingStudy", "Immunization", "ImmunizationEvaluation",
33
- "ImmunizationRecommendation", "ImplementationGuide", "Ingredient",
34
- "InsurancePlan", "InventoryItem", "InventoryReport", "Invoice", "Library",
35
- "Linkage", "List", "Location", "ManufacturedItemDefinition", "Measure",
36
- "MeasureReport", "Media", "Medication", "MedicationAdministration",
37
- "MedicationDispense", "MedicationKnowledge", "MedicationOrder",
38
- "MedicationRequest", "MedicationStatement", "MedicinalProduct",
39
- "MedicinalProductAuthorization", "MedicinalProductContraindication",
40
- "MedicinalProductDefinition", "MedicinalProductIndication",
41
- "MedicinalProductIngredient", "MedicinalProductInteraction",
42
- "MedicinalProductManufactured", "MedicinalProductPackaged",
43
- "MedicinalProductPharmaceutical", "MedicinalProductUndesirableEffect",
44
- "MessageDefinition", "MessageHeader", "MolecularSequence", "NamingSystem",
45
- "NutritionIntake", "NutritionOrder", "NutritionProduct", "Observation",
46
- "ObservationDefinition", "OperationDefinition", "OperationOutcome",
47
- "Order", "OrderResponse", "Organization", "OrganizationAffiliation",
48
- "PackagedProductDefinition", "Patient", "PaymentNotice",
49
- "PaymentReconciliation", "Permission", "Person", "PlanDefinition",
50
- "Practitioner", "PractitionerRole", "Procedure", "ProcedureRequest",
51
- "ProcessRequest", "ProcessResponse", "Provenance", "Questionnaire",
52
- "QuestionnaireResponse", "ReferralRequest", "RegulatedAuthorization",
53
- "RelatedPerson", "RequestGroup", "RequestOrchestration", "Requirements",
54
- "ResearchDefinition", "ResearchElementDefinition", "ResearchStudy",
55
- "ResearchSubject", "RiskAssessment", "RiskEvidenceSynthesis", "Schedule",
56
- "SearchParameter", "Sequence", "ServiceDefinition", "ServiceRequest",
57
- "Slot", "Specimen", "SpecimenDefinition", "StructureDefinition",
58
- "StructureMap", "Subscription", "SubscriptionStatus", "SubscriptionTopic",
59
- "Substance", "SubstanceDefinition", "SubstanceNucleicAcid",
60
- "SubstancePolymer", "SubstanceProtein", "SubstanceReferenceInformation",
61
- "SubstanceSourceMaterial", "SubstanceSpecification", "SupplyDelivery",
62
- "SupplyRequest", "Task", "TerminologyCapabilities", "TestPlan",
63
- "TestReport", "TestScript", "Transport", "ValueSet", "VerificationResult",
64
- "VisionPrescription"
8
+ 'Account', 'ActivityDefinition', 'ActorDefinition',
9
+ 'AdministrableProductDefinition', 'AdverseEvent', 'AllergyIntolerance',
10
+ 'Appointment', 'AppointmentResponse', 'ArtifactAssessment', 'AuditEvent',
11
+ 'Basic', 'Binary', 'BiologicallyDerivedProduct',
12
+ 'BiologicallyDerivedProductDispense', 'BodySite', 'BodyStructure',
13
+ 'Bundle', 'CapabilityStatement', 'CarePlan', 'CareTeam', 'CatalogEntry',
14
+ 'ChargeItem', 'ChargeItemDefinition', 'Citation', 'Claim',
15
+ 'ClaimResponse', 'ClinicalImpression', 'ClinicalUseDefinition',
16
+ 'CodeSystem', 'Communication', 'CommunicationRequest',
17
+ 'CompartmentDefinition', 'Composition', 'ConceptMap', 'Condition',
18
+ 'ConditionDefinition', 'Conformance', 'Consent', 'Contract', 'Coverage',
19
+ 'CoverageEligibilityRequest', 'CoverageEligibilityResponse',
20
+ 'DataElement', 'DetectedIssue', 'Device', 'DeviceAssociation',
21
+ 'DeviceComponent', 'DeviceDefinition', 'DeviceDispense', 'DeviceMetric',
22
+ 'DeviceRequest', 'DeviceUsage', 'DeviceUseRequest', 'DeviceUseStatement',
23
+ 'DiagnosticOrder', 'DiagnosticReport', 'DocumentManifest',
24
+ 'DocumentReference', 'EffectEvidenceSynthesis', 'EligibilityRequest',
25
+ 'EligibilityResponse', 'Encounter', 'EncounterHistory', 'Endpoint',
26
+ 'EnrollmentRequest', 'EnrollmentResponse', 'EpisodeOfCare',
27
+ 'EventDefinition', 'Evidence', 'EvidenceReport', 'EvidenceVariable',
28
+ 'ExampleScenario', 'ExpansionProfile', 'ExplanationOfBenefit',
29
+ 'FamilyMemberHistory', 'Flag', 'FormularyItem', 'GenomicStudy', 'Goal',
30
+ 'GraphDefinition', 'Group', 'GuidanceResponse', 'HealthcareService',
31
+ 'ImagingManifest', 'ImagingObjectSelection', 'ImagingSelection',
32
+ 'ImagingStudy', 'Immunization', 'ImmunizationEvaluation',
33
+ 'ImmunizationRecommendation', 'ImplementationGuide', 'Ingredient',
34
+ 'InsurancePlan', 'InventoryItem', 'InventoryReport', 'Invoice', 'Library',
35
+ 'Linkage', 'List', 'Location', 'ManufacturedItemDefinition', 'Measure',
36
+ 'MeasureReport', 'Media', 'Medication', 'MedicationAdministration',
37
+ 'MedicationDispense', 'MedicationKnowledge', 'MedicationOrder',
38
+ 'MedicationRequest', 'MedicationStatement', 'MedicinalProduct',
39
+ 'MedicinalProductAuthorization', 'MedicinalProductContraindication',
40
+ 'MedicinalProductDefinition', 'MedicinalProductIndication',
41
+ 'MedicinalProductIngredient', 'MedicinalProductInteraction',
42
+ 'MedicinalProductManufactured', 'MedicinalProductPackaged',
43
+ 'MedicinalProductPharmaceutical', 'MedicinalProductUndesirableEffect',
44
+ 'MessageDefinition', 'MessageHeader', 'MolecularSequence', 'NamingSystem',
45
+ 'NutritionIntake', 'NutritionOrder', 'NutritionProduct', 'Observation',
46
+ 'ObservationDefinition', 'OperationDefinition', 'OperationOutcome',
47
+ 'Order', 'OrderResponse', 'Organization', 'OrganizationAffiliation',
48
+ 'PackagedProductDefinition', 'Patient', 'PaymentNotice',
49
+ 'PaymentReconciliation', 'Permission', 'Person', 'PlanDefinition',
50
+ 'Practitioner', 'PractitionerRole', 'Procedure', 'ProcedureRequest',
51
+ 'ProcessRequest', 'ProcessResponse', 'Provenance', 'Questionnaire',
52
+ 'QuestionnaireResponse', 'ReferralRequest', 'RegulatedAuthorization',
53
+ 'RelatedPerson', 'RequestGroup', 'RequestOrchestration', 'Requirements',
54
+ 'ResearchDefinition', 'ResearchElementDefinition', 'ResearchStudy',
55
+ 'ResearchSubject', 'RiskAssessment', 'RiskEvidenceSynthesis', 'Schedule',
56
+ 'SearchParameter', 'Sequence', 'ServiceDefinition', 'ServiceRequest',
57
+ 'Slot', 'Specimen', 'SpecimenDefinition', 'StructureDefinition',
58
+ 'StructureMap', 'Subscription', 'SubscriptionStatus', 'SubscriptionTopic',
59
+ 'Substance', 'SubstanceDefinition', 'SubstanceNucleicAcid',
60
+ 'SubstancePolymer', 'SubstanceProtein', 'SubstanceReferenceInformation',
61
+ 'SubstanceSourceMaterial', 'SubstanceSpecification', 'SupplyDelivery',
62
+ 'SupplyRequest', 'Task', 'TerminologyCapabilities', 'TestPlan',
63
+ 'TestReport', 'TestScript', 'Transport', 'ValueSet', 'VerificationResult',
64
+ 'VisionPrescription'
65
65
  ].to_set.freeze
66
66
 
67
- FHIR_ID_REGEX = /[A-Za-z0-9\-\.]{1,64}(\/_history\/[A-Za-z0-9\-\.]{1,64})?(#[A-Za-z0-9\-\.]{1,64})?/.freeze
67
+ FHIR_ID_REGEX = %r{[A-Za-z0-9\-\.]{1,64}(/_history/[A-Za-z0-9\-\.]{1,64})?(#[A-Za-z0-9\-\.]{1,64})?}
68
68
 
69
69
  def validate_required_fields_present(body, required_fields)
70
70
  missing_fields = required_fields.select { |field| body[field].blank? }
@@ -93,7 +93,7 @@ module SMARTAppLaunch
93
93
 
94
94
  def validate_scope_subset(received_scopes, original_scopes)
95
95
  extra_scopes = received_scopes.split - original_scopes.split
96
- assert extra_scopes.empty?, "Token response contained scopes which are not a subset of the scope granted to the "\
96
+ assert extra_scopes.empty?, 'Token response contained scopes which are not a subset of the scope granted to the ' \
97
97
  "original access token: #{extra_scopes.join(', ')}"
98
98
  end
99
99
 
@@ -124,7 +124,6 @@ module SMARTAppLaunch
124
124
 
125
125
  fhir_context.each do |reference|
126
126
  assert !reference.start_with?('http'), "`#{reference}` is not a relative reference"
127
-
128
127
  resource_type, id = reference.split('/')
129
128
  assert FHIR_RESOURCE_TYPES.include?(resource_type),
130
129
  "`#{resource_type}` is not a valid FHIR resource type"
@@ -132,5 +131,75 @@ module SMARTAppLaunch
132
131
  assert id.match?(FHIR_ID_REGEX), "`#{id}` is not a valid FHIR id"
133
132
  end
134
133
  end
134
+
135
+ def check_fhir_context_reference(reference)
136
+ assert reference.is_a?(String), "`#{reference.inspect}` is not a String"
137
+ assert !reference.start_with?('http'), "`#{reference}` is not a relative reference"
138
+
139
+ resource_type, id = reference.split('/')
140
+
141
+ assert FHIR_RESOURCE_TYPES.include?(resource_type),
142
+ "`#{resource_type}` in `reference` is not a valid FHIR resource type"
143
+
144
+ assert id.match?(FHIR_ID_REGEX), "`#{id}` in `reference` is not a valid FHIR id"
145
+ end
146
+
147
+ def check_fhir_context_canonical(canonical)
148
+ assert canonical.is_a?(String), "`#{canonical.inspect}` is not a String"
149
+ assert canonical.start_with?('http'), "`#{canonical}` is not a canonical reference"
150
+
151
+ split_canonical = canonical.split('/')
152
+
153
+ if split_canonical.last.start_with?(/&|\|/)
154
+ resource_type = split_canonical[-3]
155
+ id = split_canonical[-2]
156
+ else
157
+ resource_type = split_canonical[-2]
158
+ id = split_canonical.last.split(/&|\|/).first
159
+ end
160
+
161
+ assert FHIR_RESOURCE_TYPES.include?(resource_type),
162
+ "`#{resource_type}` in `canonical` is not a valid FHIR resource type"
163
+
164
+ assert id.match?(FHIR_ID_REGEX), "`#{id}` in `canonical` is not a valid FHIR id"
165
+ end
166
+
167
+ def check_fhir_context_identifier(identifier)
168
+ assert identifier.is_a?(Hash), "`#{identifier.inspect}` is not an Object"
169
+ end
170
+
171
+ def validate_fhir_context_stu2_2(fhir_context)
172
+ return if fhir_context.nil?
173
+
174
+ assert fhir_context.is_a?(Array), "`fhirContext` field is a #{fhir_context.class.name}, but should be an Array"
175
+
176
+ fhir_context.each do |reference|
177
+ assert reference.is_a?(Hash), "`#{reference.inspect}` is not an Object"
178
+ end
179
+
180
+ fhir_context.each do |context|
181
+ reference = context['reference']
182
+ canonical = context['canonical']
183
+ identifier = context['identifier']
184
+
185
+ type = context['type']
186
+
187
+ assert reference.present? || canonical.present? || identifier.present?,
188
+ '`fhirContext` array SHALL include at least one of "reference", "canonical", or "identifier"'
189
+
190
+ check_fhir_context_reference(reference) if reference.present?
191
+ check_fhir_context_canonical(canonical) if canonical.present?
192
+ check_fhir_context_identifier(identifier) if identifier.present?
193
+
194
+ if (canonical.present? || identifier.present?) && type.blank?
195
+ info 'The `type` field is recommended when "canonical" or "identifier" is present in `fhirContext` object'
196
+ end
197
+
198
+ next unless type.present?
199
+
200
+ assert FHIR_RESOURCE_TYPES.include?(type),
201
+ "`#{type}` in `type` is not a valid FHIR resource type"
202
+ end
203
+ end
135
204
  end
136
205
  end
@@ -0,0 +1,32 @@
1
+ require_relative 'token_payload_validation'
2
+
3
+ module SMARTAppLaunch
4
+ class TokenResponseBodyTestSTU22 < TokenResponseBodyTest
5
+ title 'Token exchange response body contains required information encoded in JSON'
6
+ description %(
7
+ The EHR authorization server shall return a JSON structure that includes
8
+ an access token or a message indicating that the authorization request
9
+ has been denied. `access_token`, `token_type`, and `scope` are required.
10
+ `token_type` must be Bearer. `expires_in` is required for token
11
+ refreshes.
12
+
13
+ The format of the optional `fhirContext` field is validated if present.
14
+ )
15
+ id :smart_token_response_body_stu2_2
16
+
17
+ input :requested_scopes
18
+ output :id_token,
19
+ :refresh_token,
20
+ :access_token,
21
+ :expires_in,
22
+ :patient_id,
23
+ :encounter_id,
24
+ :received_scopes,
25
+ :intent
26
+ uses_request :token
27
+
28
+ def validate_fhir_context(fhir_context)
29
+ validate_fhir_context_stu2_2(fhir_context)
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,3 @@
1
1
  module SMARTAppLaunch
2
- VERSION = '0.4.3'.freeze
2
+ VERSION = '0.4.4'.freeze
3
3
  end
@@ -13,6 +13,7 @@ module SMARTAppLaunch
13
13
  input :url,
14
14
  title: 'FHIR Endpoint',
15
15
  description: 'URL of the FHIR endpoint used by SMART applications'
16
+
16
17
  output :well_known_configuration,
17
18
  :well_known_authorization_url,
18
19
  :well_known_introspection_url,
@@ -34,9 +35,12 @@ module SMARTAppLaunch
34
35
  base_url = "#{url.chomp('/')}/"
35
36
  config = JSON.parse(request.response_body)
36
37
 
38
+ if config['introspection_endpoint'].present?
39
+ output well_known_introspection_url: make_url_absolute(base_url, config['introspection_endpoint'])
40
+ end
41
+
37
42
  output well_known_configuration: request.response_body,
38
43
  well_known_authorization_url: make_url_absolute(base_url, config['authorization_endpoint']),
39
- well_known_introspection_url: make_url_absolute(base_url, config['introspection_endpoint']),
40
44
  well_known_management_url: make_url_absolute(base_url, config['management_endpoint']),
41
45
  well_known_registration_url: make_url_absolute(base_url, config['registration_endpoint']),
42
46
  well_known_revocation_url: make_url_absolute(base_url, config['revocation_endpoint']),
@@ -2,3 +2,5 @@ require 'tls_test_kit'
2
2
 
3
3
  require_relative 'smart_app_launch/smart_stu1_suite'
4
4
  require_relative 'smart_app_launch/smart_stu2_suite'
5
+ require_relative 'smart_app_launch/smart_stu2_2_suite'
6
+ require_relative 'smart_app_launch/smart_access_brands_suite'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_app_launch_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen MacVicar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-12 00:00:00.000000000 Z
11
+ date: 2024-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inferno_core
@@ -161,6 +161,7 @@ files:
161
161
  - lib/smart_app_launch/discovery_stu2_group.rb
162
162
  - lib/smart_app_launch/ehr_launch_group.rb
163
163
  - lib/smart_app_launch/ehr_launch_group_stu2.rb
164
+ - lib/smart_app_launch/ehr_launch_group_stu2_2.rb
164
165
  - lib/smart_app_launch/jwks.rb
165
166
  - lib/smart_app_launch/launch_received_test.rb
166
167
  - lib/smart_app_launch/openid_connect_group.rb
@@ -172,15 +173,29 @@ files:
172
173
  - lib/smart_app_launch/openid_token_header_test.rb
173
174
  - lib/smart_app_launch/openid_token_payload_test.rb
174
175
  - lib/smart_app_launch/post_auth.html
176
+ - lib/smart_app_launch/smart_access_brands_examples/r4_capability_statement.json
177
+ - lib/smart_app_launch/smart_access_brands_group.rb
178
+ - lib/smart_app_launch/smart_access_brands_retrieval_group.rb
179
+ - lib/smart_app_launch/smart_access_brands_retrieve_bundle_test.rb
180
+ - lib/smart_app_launch/smart_access_brands_suite.rb
181
+ - lib/smart_app_launch/smart_access_brands_validate_brands_test.rb
182
+ - lib/smart_app_launch/smart_access_brands_validate_bundle_test.rb
183
+ - lib/smart_app_launch/smart_access_brands_validate_endpoint_urls_test.rb
184
+ - lib/smart_app_launch/smart_access_brands_validate_endpoints_test.rb
185
+ - lib/smart_app_launch/smart_access_brands_validation_group.rb
175
186
  - lib/smart_app_launch/smart_jwks.json
176
187
  - lib/smart_app_launch/smart_stu1_suite.rb
188
+ - lib/smart_app_launch/smart_stu2_2_suite.rb
177
189
  - lib/smart_app_launch/smart_stu2_suite.rb
178
190
  - lib/smart_app_launch/standalone_launch_group.rb
179
191
  - lib/smart_app_launch/standalone_launch_group_stu2.rb
192
+ - lib/smart_app_launch/standalone_launch_group_stu2_2.rb
180
193
  - lib/smart_app_launch/token_exchange_stu2_test.rb
181
194
  - lib/smart_app_launch/token_exchange_test.rb
182
195
  - lib/smart_app_launch/token_introspection_access_token_group.rb
196
+ - lib/smart_app_launch/token_introspection_access_token_group_stu2_2.rb
183
197
  - lib/smart_app_launch/token_introspection_group.rb
198
+ - lib/smart_app_launch/token_introspection_group_stu2_2.rb
184
199
  - lib/smart_app_launch/token_introspection_request_group.rb
185
200
  - lib/smart_app_launch/token_introspection_response_group.rb
186
201
  - lib/smart_app_launch/token_payload_validation.rb
@@ -190,6 +205,7 @@ files:
190
205
  - lib/smart_app_launch/token_refresh_stu2_test.rb
191
206
  - lib/smart_app_launch/token_refresh_test.rb
192
207
  - lib/smart_app_launch/token_response_body_test.rb
208
+ - lib/smart_app_launch/token_response_body_test_stu2_2.rb
193
209
  - lib/smart_app_launch/token_response_headers_test.rb
194
210
  - lib/smart_app_launch/url_helpers.rb
195
211
  - lib/smart_app_launch/version.rb