udap_security_test_kit 0.11.2 → 0.11.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/UDAP_RunClientAgainstServer.json.erb +20 -0
  3. data/config/presets/UDAP_RunServerAgainstClient.json.erb +272 -0
  4. data/lib/udap_security_test_kit/client_credentials_token_exchange_test.rb +1 -1
  5. data/lib/udap_security_test_kit/client_suite/access_ac_group.rb +25 -0
  6. data/lib/udap_security_test_kit/client_suite/access_ac_interaction_test.rb +59 -0
  7. data/lib/udap_security_test_kit/client_suite/access_cc_group.rb +23 -0
  8. data/lib/udap_security_test_kit/client_suite/access_cc_interaction_test.rb +49 -0
  9. data/lib/udap_security_test_kit/client_suite/authorization_request_verification_test.rb +83 -0
  10. data/lib/udap_security_test_kit/client_suite/client_descriptions.rb +70 -0
  11. data/lib/udap_security_test_kit/client_suite/client_options.rb +20 -0
  12. data/lib/udap_security_test_kit/client_suite/oidc_jwks.json +32 -0
  13. data/lib/udap_security_test_kit/client_suite/oidc_jwks.rb +27 -0
  14. data/lib/udap_security_test_kit/client_suite/registration_ac_group.rb +18 -0
  15. data/lib/udap_security_test_kit/client_suite/registration_ac_verification_test.rb +38 -0
  16. data/lib/udap_security_test_kit/client_suite/registration_cc_group.rb +18 -0
  17. data/lib/udap_security_test_kit/client_suite/registration_cc_verification_test.rb +38 -0
  18. data/lib/udap_security_test_kit/client_suite/registration_interaction_test.rb +57 -0
  19. data/lib/udap_security_test_kit/client_suite/registration_request_verification.rb +242 -0
  20. data/lib/udap_security_test_kit/client_suite/token_request_ac_verification_test.rb +49 -0
  21. data/lib/udap_security_test_kit/client_suite/token_request_cc_verification_test.rb +49 -0
  22. data/lib/udap_security_test_kit/client_suite/token_request_verification.rb +223 -0
  23. data/lib/udap_security_test_kit/client_suite/token_use_verification_test.rb +40 -0
  24. data/lib/udap_security_test_kit/client_suite.rb +107 -0
  25. data/lib/udap_security_test_kit/docs/demo/FHIR Request.postman_collection.json +81 -0
  26. data/lib/udap_security_test_kit/docs/udap_client_suite_description.md +163 -0
  27. data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder_endpoint.rb +96 -0
  28. data/lib/udap_security_test_kit/endpoints/mock_udap_server/authorization_endpoint.rb +28 -0
  29. data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration_endpoint.rb +31 -0
  30. data/lib/udap_security_test_kit/endpoints/mock_udap_server/token_endpoint.rb +56 -0
  31. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_authorization_response_creation.rb +63 -0
  32. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_registration_response_creation.rb +28 -0
  33. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_token_response_creation.rb +218 -0
  34. data/lib/udap_security_test_kit/endpoints/mock_udap_server.rb +382 -0
  35. data/lib/udap_security_test_kit/metadata.rb +4 -4
  36. data/lib/udap_security_test_kit/tags.rb +12 -0
  37. data/lib/udap_security_test_kit/urls.rb +52 -0
  38. data/lib/udap_security_test_kit/version.rb +2 -2
  39. data/lib/udap_security_test_kit.rb +8 -2
  40. metadata +36 -2
@@ -0,0 +1,382 @@
1
+ require 'jwt'
2
+ require 'faraday'
3
+ require 'time'
4
+ require 'rack/utils'
5
+ require_relative '../urls'
6
+ require_relative '../tags'
7
+ require_relative '../udap_jwt_builder'
8
+
9
+ module UDAPSecurityTestKit
10
+ module MockUDAPServer
11
+ SUPPORTED_SCOPES = ['openid', 'system/*.read', 'user/*.read', 'patient/*.read'].freeze
12
+
13
+ module_function
14
+
15
+ def udap_server_metadata(suite_id)
16
+ base_url = "#{Inferno::Application['base_url']}/custom/#{suite_id}"
17
+ response_body = {
18
+ udap_versions_supported: ['1'],
19
+ udap_profiles_supported: ['udap_dcr', 'udap_authn', 'udap_authz'],
20
+ udap_authorization_extensions_supported: ['hl7-b2b'],
21
+ udap_authorization_extensions_required: [],
22
+ udap_certifications_supported: [],
23
+ udap_certifications_required: [],
24
+ grant_types_supported: ['authorization_code', 'client_credentials', 'refresh_token'],
25
+ scopes_supported: SUPPORTED_SCOPES,
26
+ registration_endpoint: base_url + REGISTRATION_PATH,
27
+ registration_endpoint_jwt_signing_alg_values_supported: ['RS256', 'RS384', 'ES384'],
28
+ authorization_endpoint: base_url + AUTHORIZATION_PATH,
29
+ token_endpoint: base_url + TOKEN_PATH,
30
+ token_endpoint_auth_methods_supported: ['private_key_jwt'],
31
+ token_endpoint_auth_signing_alg_values_supported: ['RS256', 'RS384', 'ES384'],
32
+ signed_metadata: udap_signed_metadata_jwt(base_url)
33
+ }.to_json
34
+
35
+ [200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
36
+ end
37
+
38
+ def openid_connect_metadata(suite_id)
39
+ base_url = "#{Inferno::Application['base_url']}/custom/#{suite_id}"
40
+ response_body = {
41
+ issuer: base_url + FHIR_PATH,
42
+ authorization_endpoint: base_url + AUTHORIZATION_PATH,
43
+ token_endpoint: base_url + TOKEN_PATH,
44
+ jwks_uri: base_url + OIDC_JWKS_PATH,
45
+ response_types_supported: ['code', 'id_token', 'token id_token'],
46
+ subject_types_supported: ['pairwise', 'public'],
47
+ id_token_signing_alg_values_supported: ['RS256']
48
+ }.to_json
49
+
50
+ [200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
51
+ end
52
+
53
+ def udap_signed_metadata_jwt(base_url)
54
+ jwt_claim_hash = {
55
+ iss: base_url + FHIR_PATH,
56
+ sub: base_url + FHIR_PATH,
57
+ exp: 5.minutes.from_now.to_i,
58
+ iat: Time.now.to_i,
59
+ jti: SecureRandom.hex(32),
60
+ token_endpoint: base_url + TOKEN_PATH,
61
+ authorization_endpoint: base_url + AUTHORIZATION_PATH,
62
+ registration_endpoint: base_url + REGISTRATION_PATH
63
+ }.compact
64
+
65
+ UDAPJWTBuilder.encode_jwt_with_x5c_header(
66
+ jwt_claim_hash,
67
+ test_kit_private_key,
68
+ 'RS256',
69
+ [test_kit_cert]
70
+ )
71
+ end
72
+
73
+ def root_ca_cert
74
+ File.read(
75
+ ENV.fetch('UDAP_ROOT_CA_CERT_FILE',
76
+ File.join(__dir__, '..',
77
+ 'certs', 'infernoCA.pem'))
78
+ )
79
+ end
80
+
81
+ def root_ca_private_key
82
+ File.read(
83
+ ENV.fetch('UDAP_CA_PRIVATE_KEY_FILE',
84
+ File.join(__dir__, '..',
85
+ 'certs', 'infernoCA.key'))
86
+ )
87
+ end
88
+
89
+ def test_kit_cert
90
+ File.read(
91
+ ENV.fetch('UDAP_TEST_KIT_CERT_FILE',
92
+ File.join(__dir__, '..',
93
+ 'certs', 'TestClient.pem'))
94
+ )
95
+ end
96
+
97
+ def test_kit_private_key
98
+ File.read(
99
+ ENV.fetch('UDAP_TEST_KIT_PRIVATE_KEY_FILE',
100
+ File.join(__dir__, '..',
101
+ 'certs', 'TestClientPrivateKey.key'))
102
+ )
103
+ end
104
+
105
+ def parsed_request_body(request)
106
+ JSON.parse(request.request_body)
107
+ rescue JSON::ParserError
108
+ nil
109
+ end
110
+
111
+ def parsed_io_body(request)
112
+ parsed_body = begin
113
+ JSON.parse(request.body.read)
114
+ rescue JSON::ParserError
115
+ nil
116
+ end
117
+ request.body.rewind
118
+
119
+ parsed_body
120
+ end
121
+
122
+ def jwt_claims(encoded_jwt)
123
+ JWT.decode(encoded_jwt, nil, false)[0]
124
+ end
125
+
126
+ def udap_client_uri_from_registration_payload(reg_body)
127
+ udap_claim_from_registration_payload(reg_body, 'iss')
128
+ end
129
+
130
+ def udap_claim_from_registration_payload(reg_body, claim_key)
131
+ software_statement_jwt = udap_software_statement_jwt(reg_body)
132
+ return unless software_statement_jwt.present?
133
+
134
+ jwt_claims(software_statement_jwt)&.dig(claim_key)
135
+ end
136
+
137
+ def udap_software_statement_jwt(reg_body)
138
+ reg_body&.dig('software_statement')
139
+ end
140
+
141
+ def client_uri_to_client_id(client_uri)
142
+ Base64.urlsafe_encode64(client_uri, padding: false)
143
+ end
144
+
145
+ def client_id_to_client_uri(client_id)
146
+ Base64.urlsafe_decode64(client_id)
147
+ end
148
+
149
+ def client_id_to_token(client_id, exp_min)
150
+ token_structure = {
151
+ client_id:,
152
+ expiration: exp_min.minutes.from_now.to_i,
153
+ nonce: SecureRandom.hex(8)
154
+ }.to_json
155
+
156
+ Base64.urlsafe_encode64(token_structure, padding: false)
157
+ end
158
+
159
+ def decode_token(token)
160
+ token_to_decode =
161
+ if issued_token_is_refresh_token(token)
162
+ refresh_token_to_authorization_code(token)
163
+ else
164
+ token
165
+ end
166
+ return unless token_to_decode.present?
167
+
168
+ JSON.parse(Base64.urlsafe_decode64(token))
169
+ rescue JSON::ParserError
170
+ nil
171
+ end
172
+
173
+ def issued_token_to_client_id(token)
174
+ decode_token(token)&.dig('client_id')
175
+ end
176
+
177
+ def issued_token_is_refresh_token(token)
178
+ token.end_with?('_rt')
179
+ end
180
+
181
+ def authorization_code_to_refresh_token(code)
182
+ "#{code}_rt"
183
+ end
184
+
185
+ def refresh_token_to_authorization_code(refresh_token)
186
+ refresh_token[..-4]
187
+ end
188
+
189
+ def request_has_expired_token?(request)
190
+ return false if request.params[:session_path].present?
191
+
192
+ token = request.headers['authorization']&.delete_prefix('Bearer ')
193
+ token_expired?(token)
194
+ end
195
+
196
+ def token_expired?(token, check_time = nil)
197
+ decoded_token = decode_token(token)
198
+ return false unless decoded_token&.dig('expiration').present?
199
+
200
+ check_time = Time.now.to_i unless check_time.present?
201
+ decoded_token['expiration'] < check_time
202
+ end
203
+
204
+ def update_response_for_expired_token(response, type)
205
+ response.status = 401
206
+ response.format = :json
207
+ response.body = FHIR::OperationOutcome.new(
208
+ issue: FHIR::OperationOutcome::Issue.new(severity: 'fatal', code: 'expired',
209
+ details: FHIR::CodeableConcept.new(text: "#{type}has expired"))
210
+ ).to_json
211
+ end
212
+
213
+ def udap_reg_signature_verification(assertion_jwt)
214
+ assertion_body, assertion_header = JWT.decode(assertion_jwt, nil, false)
215
+ return 'missing `x5c` header' if assertion_header['x5c'].blank?
216
+
217
+ leaf_cert_der = Base64.decode64(assertion_header['x5c'].first)
218
+ leaf_cert = OpenSSL::X509::Certificate.new(leaf_cert_der)
219
+
220
+ signature_error = udap_assertion_signature_verification(assertion_jwt, leaf_cert, assertion_header['alg'])
221
+ return signature_error if signature_error.present?
222
+
223
+ # check the certificate's SAN extension for the issuer name
224
+ issuer = assertion_body['iss']
225
+ begin
226
+ alt_names =
227
+ leaf_cert.extensions
228
+ .find { |extension| extension.oid == 'subjectAltName' }.value
229
+ rescue NoMethodError
230
+ return 'Could not find Subject Alternative Name extension in leaf certificate'
231
+ end
232
+ return if alt_names.include?("URI:#{issuer}")
233
+
234
+ "`iss` claim `#{issuer}` not found in Subject Alternative Name extension " \
235
+ "from the `x5c` JWT header: `#{alt_names}`"
236
+ end
237
+
238
+ def udap_token_signature_verification(assertion_jwt, registration_jwt)
239
+ _assertion_body, assertion_header = JWT.decode(assertion_jwt, nil, false)
240
+ return 'missing `x5c` header' if assertion_header['x5c'].blank?
241
+
242
+ leaf_cert_der = Base64.decode64(assertion_header['x5c'].first)
243
+ leaf_cert = OpenSSL::X509::Certificate.new(leaf_cert_der)
244
+
245
+ signature_error = udap_assertion_signature_verification(assertion_jwt, leaf_cert, assertion_header['alg'])
246
+ return signature_error if signature_error.present?
247
+ return unless registration_jwt.present?
248
+
249
+ # check trust
250
+ _registration_body, registration_header = JWT.decode(registration_jwt, nil, false)
251
+ return if assertion_header['x5c'].first == registration_header['x5c'].first
252
+
253
+ 'signing cert does not match registration cert'
254
+ end
255
+
256
+ def udap_assertion_signature_verification(assertion_jwt, signing_cert, algorithm)
257
+ return 'missing `alg` header' unless algorithm.present?
258
+
259
+ signature_validation_result = UDAPSecurityTestKit::UDAPJWTValidator.validate_signature(
260
+ assertion_jwt,
261
+ algorithm,
262
+ signing_cert
263
+ )
264
+ return if signature_validation_result[:success]
265
+
266
+ signature_validation_result[:error_message]
267
+ end
268
+
269
+ def udap_registration_software_statement(test_session_id)
270
+ registration_requests =
271
+ Inferno::Repositories::Requests.new.tagged_requests(test_session_id, [UDAP_TAG, REGISTRATION_TAG])
272
+ return unless registration_requests.present?
273
+
274
+ parsed_body = MockUDAPServer.parsed_request_body(registration_requests.last)
275
+ parsed_body&.dig('software_statement')
276
+ end
277
+
278
+ def update_response_for_error(response, error_message)
279
+ response.status = 401
280
+ response.format = :json
281
+ response.body = { error: 'invalid_client', error_description: error_message }.to_json
282
+ end
283
+
284
+ def client_id_from_client_assertion(client_assertion_jwt)
285
+ return unless client_assertion_jwt.present?
286
+
287
+ jwt_claims(client_assertion_jwt)&.dig('iss')
288
+ end
289
+
290
+ def check_jwt_timing(issue_claim, expiration_claim, request_time) # rubocop:disable Metrics/CyclomaticComplexity
291
+ add_message('error', 'Registration software statement `iat` claim is missing') unless issue_claim.present?
292
+ add_message('error', 'Registration software statement `exp` claim is missing') unless expiration_claim.present?
293
+ return unless issue_claim.present? && expiration_claim.present?
294
+
295
+ unless issue_claim.is_a?(Numeric)
296
+ add_message('error',
297
+ "Registration software statement `iat` claim is invalid: expected a number, got '#{issue_claim}'")
298
+ end
299
+ unless expiration_claim.is_a?(Numeric)
300
+ add_message('error',
301
+ 'Registration software statement `exp` claim is invalid: ' \
302
+ "expected a number, got '#{expiration_claim}'")
303
+ end
304
+ return unless issue_claim.is_a?(Numeric) && expiration_claim.is_a?(Numeric)
305
+
306
+ issue_time = Time.at(issue_claim)
307
+ expiration_time = Time.at(expiration_claim)
308
+ unless expiration_time > issue_time
309
+ add_message('error',
310
+ 'Registration software statement `exp` claim is invalid: ' \
311
+ 'cannot be before the `iat` claim.')
312
+ end
313
+ unless expiration_time <= issue_time + 5.minutes
314
+ add_message('error',
315
+ 'Registration software statement `exp` claim is invalid: ' \
316
+ 'cannot be more than 5 minutes after the `iat` claim.')
317
+ end
318
+ unless issue_time <= request_time
319
+ add_message('error',
320
+ 'Registration software statement `iat` claim is invalid: ' \
321
+ 'cannot be after the request time.')
322
+ end
323
+ unless expiration_time > request_time
324
+ add_message('error',
325
+ 'Registration software statement `exp` claim is invalid: ' \
326
+ 'it has expired.')
327
+ end
328
+
329
+ nil
330
+ end
331
+
332
+ def pkce_error(verifier, challenge, method)
333
+ if verifier.blank?
334
+ 'pkce check failed: no verifier provided'
335
+ elsif challenge.blank?
336
+ 'pkce check failed: no challenge code provided'
337
+ elsif method == 'S256'
338
+ return nil unless challenge != calculate_s256_challenge(verifier)
339
+
340
+ "invalid S256 pkce verifier: got '#{calculate_s256_challenge(verifier)}' " \
341
+ "expected '#{challenge}'"
342
+ else
343
+ "invalid pkce challenge method '#{method}'"
344
+ end
345
+ end
346
+
347
+ def pkce_valid?(verifier, challenge, method, response)
348
+ pkce_error = pkce_error(verifier, challenge, method)
349
+
350
+ if pkce_error.present?
351
+ update_response_for_error(response, pkce_error)
352
+ false
353
+ else
354
+ true
355
+ end
356
+ end
357
+
358
+ def calculate_s256_challenge(verifier)
359
+ Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), padding: false)
360
+ end
361
+
362
+ def authorization_request_for_code(code, test_session_id)
363
+ authorization_requests = Inferno::Repositories::Requests.new.tagged_requests(test_session_id, [AUTHORIZATION_TAG])
364
+ authorization_requests.find do |request|
365
+ location_header = request.response_headers.find { |header| header.name.downcase == 'location' }
366
+ if location_header.present? && location_header.value.present?
367
+ Rack::Utils.parse_query(URI(location_header.value)&.query)&.dig('code') == code
368
+ else
369
+ false
370
+ end
371
+ end
372
+ end
373
+
374
+ def authorization_code_request_details(inferno_request)
375
+ if inferno_request.verb.downcase == 'get'
376
+ Rack::Utils.parse_query(URI(inferno_request.url)&.query)
377
+ elsif inferno_request.verb.downcase == 'post'
378
+ Rack::Utils.parse_query(inferno_request.request_body)
379
+ end
380
+ end
381
+ end
382
+ end
@@ -5,23 +5,23 @@ module UDAPSecurityTestKit
5
5
  id :udap_security
6
6
  title 'UDAP Security Test Kit'
7
7
  description <<~DESCRIPTION
8
- This is a collection of tests to verify server conformance to the [HL7 UDAP Security
8
+ This is a collection of tests to verify client and server conformance to the [HL7 UDAP Security
9
9
  STU 1.0 IG](https://hl7.org/fhir/us/udap-security/STU1/index.html)
10
10
  <!-- break -->
11
11
  Specifically, this test
12
- kit assesses the required capabilities from the following sections:
12
+ kit assesses the required capabilities for clients and servers from the following sections:
13
13
  - [JSON Web Token (JWT) Requirements](https://hl7.org/fhir/us/udap-security/STU1/index.html)
14
14
  - [Discovery](https://hl7.org/fhir/us/udap-security/STU1/discovery.html)
15
15
  - [Dynamic Client Registration](https://hl7.org/fhir/us/udap-security/STU1/registration.html)
16
16
  - [Consumer-Facing Authorization & Authentication](https://hl7.org/fhir/us/udap-security/STU1/consumer.html)
17
+ (server only)
17
18
  - [Business-to-Business (B2B) Authorization & Authentication](https://hl7.org/fhir/us/udap-security/STU1/b2b.html)
18
19
 
19
20
  [Tiered OAuth for User
20
21
  Authentication](https://hl7.org/fhir/us/udap-security/STU1/user.html) is not a
21
22
  required capability and is not assessed.
22
- This test kit also does not assess client conformance.
23
23
  DESCRIPTION
24
- suite_ids [:udap_security]
24
+ suite_ids [:udap_security, :udap_security_client]
25
25
  tags ['UDAP Security']
26
26
  last_updated LAST_UPDATED
27
27
  version VERSION
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UDAPSecurityTestKit
4
+ REGISTRATION_TAG = 'registration'
5
+ AUTHORIZATION_TAG = 'authorization'
6
+ TOKEN_TAG = 'token'
7
+ UDAP_TAG = 'udap'
8
+ ACCESS_TAG = 'access'
9
+ CLIENT_CREDENTIALS_TAG = 'client_credentials'
10
+ AUTHORIZATION_CODE_TAG = 'authorization_code'
11
+ REFRESH_TOKEN_TAG = 'refresh_token'
12
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UDAPSecurityTestKit
4
+ FHIR_PATH = '/fhir'
5
+ OIDC_DISCOVERY_PATH = "#{FHIR_PATH}/.well-known/openid-configuration".freeze
6
+ OIDC_JWKS_PATH = "#{FHIR_PATH}/.well-known/jwks.json".freeze
7
+ UDAP_DISCOVERY_PATH = "#{FHIR_PATH}/.well-known/udap".freeze
8
+ AUTH_SERVER_PATH = '/auth'
9
+ REGISTRATION_PATH = "#{AUTH_SERVER_PATH}/register".freeze
10
+ AUTHORIZATION_PATH = "#{AUTH_SERVER_PATH}/authorization".freeze
11
+ TOKEN_PATH = "#{AUTH_SERVER_PATH}/token".freeze
12
+ RESUME_PASS_PATH = '/resume_pass'
13
+ RESUME_FAIL_PATH = '/resume_fail'
14
+
15
+ module URLs
16
+ def client_base_url
17
+ @client_base_url ||= "#{Inferno::Application['base_url']}/custom/#{client_suite_id}"
18
+ end
19
+
20
+ def client_fhir_base_url
21
+ @client_fhir_base_url ||= client_base_url + FHIR_PATH
22
+ end
23
+
24
+ def client_resume_pass_url
25
+ @client_resume_pass_url ||= client_base_url + RESUME_PASS_PATH
26
+ end
27
+
28
+ def client_resume_fail_url
29
+ @client_resume_fail_url ||= client_base_url + RESUME_FAIL_PATH
30
+ end
31
+
32
+ def client_udap_discovery_url
33
+ @client_udap_discovery_url ||= client_base_url + UDAP_DISCOVERY_PATH
34
+ end
35
+
36
+ def client_registration_url
37
+ @client_registration_url ||= client_base_url + REGISTRATION_PATH
38
+ end
39
+
40
+ def client_authorization_url
41
+ @client_authorization_url ||= client_base_url + AUTHORIZATION_PATH
42
+ end
43
+
44
+ def client_token_url
45
+ @client_token_url ||= client_base_url + TOKEN_PATH
46
+ end
47
+
48
+ def client_suite_id
49
+ UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
50
+ end
51
+ end
52
+ end
@@ -1,4 +1,4 @@
1
1
  module UDAPSecurityTestKit
2
- VERSION = '0.11.2'.freeze
3
- LAST_UPDATED = '2025-03-11'.freeze
2
+ VERSION = '0.11.4'.freeze
3
+ LAST_UPDATED = '2025-05-02'.freeze
4
4
  end
@@ -1,3 +1,4 @@
1
+ require_relative 'udap_security_test_kit/client_suite'
1
2
  require_relative 'udap_security_test_kit/authorization_code_group'
2
3
  require_relative 'udap_security_test_kit/client_credentials_group'
3
4
  require_relative 'udap_security_test_kit/redirect_uri'
@@ -6,7 +7,7 @@ require_relative 'udap_security_test_kit/metadata'
6
7
  module UDAPSecurityTestKit
7
8
  class Suite < Inferno::TestSuite
8
9
  id :udap_security
9
- title 'UDAP Security'
10
+ title 'UDAP Security Server'
10
11
  description %(
11
12
  The User Data Access Protocol (UDAP) Security test kit verifies that systems correctly implement the
12
13
  [HL7 UDAP Security IG](http://hl7.org/fhir/us/udap-security/STU1/)
@@ -28,7 +29,7 @@ module UDAPSecurityTestKit
28
29
  Testers may test one or both flows based on their system under test.
29
30
 
30
31
  This test suite does NOT assess [Tiered OAuth for User Authentication](https://hl7.org/fhir/us/udap-security/STU1/user.html)
31
- (which is not a required capability) or client conformance to the HL7 UDAP IG.
32
+ (which is not a required capability).
32
33
  )
33
34
 
34
35
  input_instructions %(
@@ -74,6 +75,11 @@ module UDAPSecurityTestKit
74
75
  type: 'download',
75
76
  label: 'Download',
76
77
  url: 'https://github.com/inferno-framework/udap-security-test-kit/releases/'
78
+ },
79
+ {
80
+ type: 'ig',
81
+ label: 'Implementation Guide',
82
+ url: 'https://hl7.org/fhir/us/udap-security/STU1/'
77
83
  }
78
84
  ]
79
85
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: udap_security_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.2
4
+ version: 0.11.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen MacVicar
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-03-11 00:00:00.000000000 Z
12
+ date: 2025-05-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: inferno_core
@@ -48,6 +48,8 @@ extra_rdoc_files: []
48
48
  files:
49
49
  - LICENSE
50
50
  - config/presets/SureFhirIdentityMatchingDemo.json
51
+ - config/presets/UDAP_RunClientAgainstServer.json.erb
52
+ - config/presets/UDAP_RunServerAgainstClient.json.erb
51
53
  - lib/udap_security_test_kit.rb
52
54
  - lib/udap_security_test_kit/authorization_code_authentication_group.rb
53
55
  - lib/udap_security_test_kit/authorization_code_group.rb
@@ -62,10 +64,40 @@ files:
62
64
  - lib/udap_security_test_kit/client_credentials_authentication_group.rb
63
65
  - lib/udap_security_test_kit/client_credentials_group.rb
64
66
  - lib/udap_security_test_kit/client_credentials_token_exchange_test.rb
67
+ - lib/udap_security_test_kit/client_suite.rb
68
+ - lib/udap_security_test_kit/client_suite/access_ac_group.rb
69
+ - lib/udap_security_test_kit/client_suite/access_ac_interaction_test.rb
70
+ - lib/udap_security_test_kit/client_suite/access_cc_group.rb
71
+ - lib/udap_security_test_kit/client_suite/access_cc_interaction_test.rb
72
+ - lib/udap_security_test_kit/client_suite/authorization_request_verification_test.rb
73
+ - lib/udap_security_test_kit/client_suite/client_descriptions.rb
74
+ - lib/udap_security_test_kit/client_suite/client_options.rb
75
+ - lib/udap_security_test_kit/client_suite/oidc_jwks.json
76
+ - lib/udap_security_test_kit/client_suite/oidc_jwks.rb
77
+ - lib/udap_security_test_kit/client_suite/registration_ac_group.rb
78
+ - lib/udap_security_test_kit/client_suite/registration_ac_verification_test.rb
79
+ - lib/udap_security_test_kit/client_suite/registration_cc_group.rb
80
+ - lib/udap_security_test_kit/client_suite/registration_cc_verification_test.rb
81
+ - lib/udap_security_test_kit/client_suite/registration_interaction_test.rb
82
+ - lib/udap_security_test_kit/client_suite/registration_request_verification.rb
83
+ - lib/udap_security_test_kit/client_suite/token_request_ac_verification_test.rb
84
+ - lib/udap_security_test_kit/client_suite/token_request_cc_verification_test.rb
85
+ - lib/udap_security_test_kit/client_suite/token_request_verification.rb
86
+ - lib/udap_security_test_kit/client_suite/token_use_verification_test.rb
65
87
  - lib/udap_security_test_kit/common_assertions.rb
66
88
  - lib/udap_security_test_kit/default_cert_file_loader.rb
67
89
  - lib/udap_security_test_kit/discovery_group.rb
90
+ - lib/udap_security_test_kit/docs/demo/FHIR Request.postman_collection.json
91
+ - lib/udap_security_test_kit/docs/udap_client_suite_description.md
68
92
  - lib/udap_security_test_kit/dynamic_client_registration_group.rb
93
+ - lib/udap_security_test_kit/endpoints/echoing_fhir_responder_endpoint.rb
94
+ - lib/udap_security_test_kit/endpoints/mock_udap_server.rb
95
+ - lib/udap_security_test_kit/endpoints/mock_udap_server/authorization_endpoint.rb
96
+ - lib/udap_security_test_kit/endpoints/mock_udap_server/registration_endpoint.rb
97
+ - lib/udap_security_test_kit/endpoints/mock_udap_server/token_endpoint.rb
98
+ - lib/udap_security_test_kit/endpoints/mock_udap_server/udap_authorization_response_creation.rb
99
+ - lib/udap_security_test_kit/endpoints/mock_udap_server/udap_registration_response_creation.rb
100
+ - lib/udap_security_test_kit/endpoints/mock_udap_server/udap_token_response_creation.rb
69
101
  - lib/udap_security_test_kit/grant_types_supported_field_test.rb
70
102
  - lib/udap_security_test_kit/igs/put_ig_package_dot_tgz_here
71
103
  - lib/udap_security_test_kit/metadata.rb
@@ -81,6 +113,7 @@ files:
81
113
  - lib/udap_security_test_kit/signed_metadata_field_test.rb
82
114
  - lib/udap_security_test_kit/signed_metadata_trust_verification_test.rb
83
115
  - lib/udap_security_test_kit/software_statement_builder.rb
116
+ - lib/udap_security_test_kit/tags.rb
84
117
  - lib/udap_security_test_kit/token_endpoint_auth_methods_supported_field_test.rb
85
118
  - lib/udap_security_test_kit/token_endpoint_auth_signing_alg_values_supported_field_test.rb
86
119
  - lib/udap_security_test_kit/token_endpoint_field_test.rb
@@ -97,6 +130,7 @@ files:
97
130
  - lib/udap_security_test_kit/udap_request_builder.rb
98
131
  - lib/udap_security_test_kit/udap_versions_supported_field_test.rb
99
132
  - lib/udap_security_test_kit/udap_x509_certificate.rb
133
+ - lib/udap_security_test_kit/urls.rb
100
134
  - lib/udap_security_test_kit/version.rb
101
135
  - lib/udap_security_test_kit/well_known_endpoint_test.rb
102
136
  homepage: https://github.com/inferno-framework/udap-security-test-kit