udap_security_test_kit 0.11.3 → 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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/UDAP_RunServerAgainstClient.json.erb +4 -4
  3. data/lib/udap_security_test_kit/client_suite/access_ac_group.rb +25 -0
  4. data/lib/udap_security_test_kit/client_suite/access_ac_interaction_test.rb +59 -0
  5. data/lib/udap_security_test_kit/client_suite/access_cc_group.rb +23 -0
  6. data/lib/udap_security_test_kit/client_suite/access_cc_interaction_test.rb +49 -0
  7. data/lib/udap_security_test_kit/client_suite/authorization_request_verification_test.rb +83 -0
  8. data/lib/udap_security_test_kit/client_suite/client_descriptions.rb +70 -0
  9. data/lib/udap_security_test_kit/client_suite/client_options.rb +20 -0
  10. data/lib/udap_security_test_kit/client_suite/oidc_jwks.json +32 -0
  11. data/lib/udap_security_test_kit/client_suite/oidc_jwks.rb +27 -0
  12. data/lib/udap_security_test_kit/client_suite/registration_ac_group.rb +18 -0
  13. data/lib/udap_security_test_kit/client_suite/registration_ac_verification_test.rb +38 -0
  14. data/lib/udap_security_test_kit/client_suite/registration_cc_group.rb +18 -0
  15. data/lib/udap_security_test_kit/client_suite/registration_cc_verification_test.rb +38 -0
  16. data/lib/udap_security_test_kit/client_suite/{client_registration_interaction_test.rb → registration_interaction_test.rb} +11 -4
  17. data/lib/udap_security_test_kit/client_suite/{client_registration_verification_test.rb → registration_request_verification.rb} +38 -40
  18. data/lib/udap_security_test_kit/client_suite/token_request_ac_verification_test.rb +49 -0
  19. data/lib/udap_security_test_kit/client_suite/token_request_cc_verification_test.rb +49 -0
  20. data/lib/udap_security_test_kit/client_suite/{client_token_request_verification_test.rb → token_request_verification.rb} +91 -46
  21. data/lib/udap_security_test_kit/client_suite/{client_token_use_verification_test.rb → token_use_verification_test.rb} +0 -3
  22. data/lib/udap_security_test_kit/client_suite.rb +46 -17
  23. data/lib/udap_security_test_kit/docs/udap_client_suite_description.md +74 -31
  24. data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder_endpoint.rb +96 -0
  25. data/lib/udap_security_test_kit/endpoints/mock_udap_server/authorization_endpoint.rb +28 -0
  26. data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration_endpoint.rb +31 -0
  27. data/lib/udap_security_test_kit/endpoints/mock_udap_server/token_endpoint.rb +56 -0
  28. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_authorization_response_creation.rb +63 -0
  29. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_registration_response_creation.rb +28 -0
  30. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_token_response_creation.rb +218 -0
  31. data/lib/udap_security_test_kit/endpoints/mock_udap_server.rb +112 -31
  32. data/lib/udap_security_test_kit/metadata.rb +1 -1
  33. data/lib/udap_security_test_kit/tags.rb +4 -0
  34. data/lib/udap_security_test_kit/urls.rb +15 -8
  35. data/lib/udap_security_test_kit/version.rb +2 -2
  36. metadata +28 -12
  37. data/lib/udap_security_test_kit/client_suite/client_access_group.rb +0 -22
  38. data/lib/udap_security_test_kit/client_suite/client_access_interaction_test.rb +0 -53
  39. data/lib/udap_security_test_kit/client_suite/client_registration_group.rb +0 -26
  40. data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder.rb +0 -52
  41. data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration.rb +0 -57
  42. data/lib/udap_security_test_kit/endpoints/mock_udap_server/token.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03620024d784bdef2def46c35503c0291eacf4bf0f613ccc546febd4b9502066
4
- data.tar.gz: fe308df520fcfe3120aacfb66cc7191ab03de1eedee46da7b9775d88bb798dba
3
+ metadata.gz: 93cc168a29a41ac4c22bdcb8a4124aa85c370d3a0ae8ac1851090555ab3a2e8b
4
+ data.tar.gz: d31eee97042a453fe3ccf6c72c1421309ff4d367eb87ff6d2f3a63a96529041d
5
5
  SHA512:
6
- metadata.gz: 9482d4811cbcf5fb500b237684722e2835289470a4ffe72c3c53338aeecb433c5f5f855f88a741d346d1b614ff67a9ecb1e0c354296f85c3c40f45e4faea07d4
7
- data.tar.gz: a41ee216e78fa02afec7d30e0ee1067dd306d7e72ac868c042e90efce1291d201a4347c8f73fee6b1688baba7bf50a523b37f602fed82d72d19db0f2d72f558d
6
+ metadata.gz: 69293481ba6a4edeca09cf992cd7b7ff97ff380636a43e1d40cb6dd2610711bb1feac263cdf887fbb7e74e8c72ed15329335b8d3b4fdd3a4d3ed1f3f73341eef
7
+ data.tar.gz: 90878299bfb40e53ad43ff65144d008f3793a1654c8f61ff3dd24a4a8cb6f3dcef11c092cac49ccdadb7dec165ed20134213cc56a566b8e78792e0b83c6ca872
@@ -95,28 +95,28 @@
95
95
  "description": "A list of one or more X.509 certificates in PEM format separated by a newline. The first (leaf) certificate MUST represent the client entity Inferno will register as, and the trust chain that will be built from the provided certificate(s) must resolve to a CA trusted by the authorization server under test.",
96
96
  "title": "Authorization Code Client Certificate(s) (PEM Format)",
97
97
  "type": "textarea",
98
- "value": ""
98
+ "value": "-----BEGIN CERTIFICATE-----\nMIIFcjCCA1qgAwIBAgIUbdCfB3IJ9bdOPGQVtxJHhMxUWv0wDQYJKoZIhvcNAQEL\nBQAwgYYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTEQMA4GA1UEBwwHQmVkZm9y\nZDEQMA4GA1UECgwHSW5mZXJubzEdMBsGA1UEAwwUSW5mZXJuby1VREFQLVJvb3Qt\nQ0ExJzAlBgkqhkiG9w0BCQEWGGluZmVybm9AZ3JvdXBzLm1pdHJlLm9yZzAeFw0y\nNDA4MTIyMzU4MTBaFw0zNDA4MTAyMzU4MTBaMGExCzAJBgNVBAYTAlVTMQswCQYD\nVQQIDAJNQTEQMA4GA1UEBwwHQmVkZm9yZDEQMA4GA1UECgwHSW5mZXJubzEhMB8G\nA1UEAwwYVURBUCBFeGFtcGxlIFRlc3QgQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAnfqzJCyeFNRhlcsrUPiO2LQtudObDHUKj4Q7fkYPUf9X\necTMckfPKd8UJ7x8Vb5o1zmR3hsMoo1A7IkwBkmK2BXvxq243cCGO1q4w/jdL/EG\nDiIZjTr7qvkawyeh9cAaApOrBlD4gnOxB05GjingDZgiT7GqBwrpEB2XJ4tw4idS\nB3W9Rv0ynbgqPKgGw9hnEef+uNAvFIvSbfdz1n4xHNP0GuMuAX+edFCyxYFmDe74\n8pl3TH9dxkoM945r5tHmuJS9n1pXkTB9L5RVbqH77dIyOBobehHHpT4D3zjdSFmh\nfta5NnDi5/iqRcFz7FVO79jJIoAqN+mWBlVH0yyfYQIDAQABo4H7MIH4MC8GA1Ud\nEQQoMCaGJGh0dHBzOi8vaW5mZXJuby5jb20vdWRhcF9zZWN1cml0eS9hYzAxBgNV\nHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBDA6\nBgNVHR8EMzAxMC+gLaArhilodHRwczovL2luZmVybm8uY29tL21vY2tfY3JsX2Vu\nZHBvaW50LmNybDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU6Eo2\nHE37gpSaW9zBGf0t7XfYnfIwHwYDVR0jBBgwFoAUNOqoPKju4u9y9JAuAfnSAcFb\nNKAwDQYJKoZIhvcNAQELBQADggIBACh2uQ1Krkw6F3Gq0HG3ohCm2j1ynmLSJwGE\nHvPlkiBcs8RBPxZzJOZJBMxGmjTPga2Kt6zsBlmjLg++C7C5/8JruwrMtrBuTtHx\nKky0Qw+YJm81IgATeDIU/qkJB8LcnHgkQbu3nyoyeKocx9XSW8nlEm4FkyREXxfC\nrSVCoc70GGg2vSnSkRikNjxwKGnHvmEDUOW2bBbbzvTGKlTKSIF50NiNPM3Fi7vF\nbOfi1aZh6m8IKVaI2KUXVFHco1qB3QDK5BUEimko+EyaWSZPvP83PE2+TIdapRrw\nHxQQJEr774GUNH1/hdW0qlP4u3CMMMAjS2H4dOsRYOOmgC6iEewFEBQqTRLkEfgP\npMxYhGAVAmrglLLr4t+ZF9KOmifvB6f18qF352Bj1D0TMB+oLy67kFw3s2ah21la\n3Xm6hQt+M5mZ/EYZIOUPxMHtqVt5DSJdMENHO2cjcRARyeD/BGYnJqnf6yA1LL2V\nTK+jhC/C/Dcv0hHQnVWlUGlwoFMOOzfsr2K3mXYezAuHASP8LIAyjodPpd6cLQu2\nSZTVVobSebaNmZ2UeX8Bc9bcobM2bbVb2c3UeezEJWOpt5cSOeEScEiwkaktxD5p\nix1K3KkIg2yDP176ILlVqBBA0X2FqTSSvFbTa5us3XIwkDfARJBpLnA7OfmsO61+\nIj5io7+X\n-----END CERTIFICATE-----"
99
99
  },
100
100
  {
101
101
  "name": "udap_auth_code_flow_client_private_key",
102
102
  "description": "The private key corresponding to the client certificate used for registration, in PEM format. Used to sign registration and/or authentication JWTs.",
103
103
  "title": "Authorization Code Client Private Key (PEM Format)",
104
104
  "type": "textarea",
105
- "value": ""
105
+ "value": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCd+rMkLJ4U1GGV\nyytQ+I7YtC2505sMdQqPhDt+Rg9R/1d5xMxyR88p3xQnvHxVvmjXOZHeGwyijUDs\niTAGSYrYFe/GrbjdwIY7WrjD+N0v8QYOIhmNOvuq+RrDJ6H1wBoCk6sGUPiCc7EH\nTkaOKeANmCJPsaoHCukQHZcni3DiJ1IHdb1G/TKduCo8qAbD2GcR5/640C8Ui9Jt\n93PWfjEc0/Qa4y4Bf550ULLFgWYN7vjymXdMf13GSgz3jmvm0ea4lL2fWleRMH0v\nlFVuofvt0jI4Ght6EcelPgPfON1IWaF+1rk2cOLn+KpFwXPsVU7v2MkigCo36ZYG\nVUfTLJ9hAgMBAAECggEAApTpPosYHkEGQztpvs4BD5uKL8I8g2yaOpQvoLWmZHGm\nzU+hA7EWuplxq+CRq5kL/5BqSNXqU/G5AOSRC1lCUpuxKm8GWWFfEDNAV7uGadUn\ngy2de0heeoHNpSjNpcV451fgcJ78IK2hU/w8fPBEQBSfYuwFWk4cVu4U3UmTE68I\nO7PpCdTjk70IOsLT8uBagAOw92Fj0mGU7agCdWfJ+LjPRUtg+PhVvODatL3taG+l\nFAParsomiRqKGINyn8vALXNISuBYHVDUzv93P10gG5rA2sJ7953/+++Hr9DoLpmL\nSH/q/LebFzPJ2u6KzLc6bam1YX0w/MSNTJVIxA5V/QKBgQDU+iFXdShLwvEXGVtb\nFdZuRk3XsN+yfkFyM9SZrmaLF2VBALkC5pd+rYkXSaTA/cbs623VOYpoJhVmjL8d\nVvTlN/G+8TzfdsmOlAXiOSiUn3VKM/Othu5l9k0AgylU8aXBWTUv5FzmV8gMn+/s\n9oOucAVFa60LdkR2d609y/tKnwKBgQC95Gviall3E/+7drT05KxPPGDKRUjhCLBe\nBNj2+ttSt4+v+E5v3K+LSd11VfGkvLkDvkhjiJlVamf+XgLne3TLrEThAKq8ySUr\nXKBRi+nfmisUHSrMGljHw500Rx+MR/LEpfEERMosLQWTrOTz5VG2RueVHbWbD622\nWitr8tnV/wKBgGDYUNr9GlLBFXJEhIc5ueUxMOp4sm/u+4Gb0fwEEvsCq3dQhdCs\n3IytCp69TR65B4DqWWpRHP/Y+XhFXg5QYVHuC46hEeYnlOWxp69EAJD8pZAVaaQp\nrDRPOJqYCe5nZ9Ew6H+bnybbGcur2qTtP9nNdIgpu2lv4RfhubRVEjLPAoGAXqzb\nSSii8GbNMwcNU6gLbPn6e/6tRl1RqZ6bGhCadxREFIUlfko2T6kFPDIcZ3kceYxO\nhSme4WJK9RykMAtygPWj5daySauz13m4CNBMS4qO/dlI9DgSmY6i+2SWixd4J6lg\nkDNH5VyREj66bAuigNG7NrJ4UBYyEt/EFG8hQrsCgYEAyLwk7f2Wgu9ykdwSrrQE\nTBK0iOVUcwPhxj1UbFyDxPdO0EspyKtIFD5w/sbBtQbJGSUFd7ZPoCsYkT2yAsDp\n3cI/ubRxA+/GaqUo84QxvJ4Uqdu8YS8C0FCEnhXGjA60Y5HFzufJF5EqfzwNctCr\nG0cRyX/4Ut4BNUcir/CRVL0=\n-----END PRIVATE KEY-----"
106
106
  },
107
107
  {
108
108
  "name": "udap_auth_code_flow_cert_iss",
109
109
  "description": "MUST correspond to a unique URI entry in the Subject Alternative Name (SAN) extension of the client certificate used for registration.",
110
110
  "title": "Authorization Code JWT Issuer (iss) Claim",
111
111
  "type": "text",
112
- "value": ""
112
+ "value": "<%= Inferno::Application['base_url'] %>/custom/udap_security/fhir"
113
113
  },
114
114
  {
115
115
  "name": "udap_auth_code_flow_registration_scope",
116
116
  "description": "String containing a space delimited list of scopes requested by the client application for use in subsequent requests. The Authorization Server MAY consider this list when deciding the scopes that it will allow the application to subsequently request. Apps requesting the \"authorization_code\" grant type SHOULD request user or patient scopes.",
117
117
  "title": "Authorization Code Registration Requested Scope(s)",
118
118
  "type": "text",
119
- "value": ""
119
+ "value": "patient/*.read"
120
120
  },
121
121
  {
122
122
  "name": "udap_jwt_signing_alg",
@@ -0,0 +1,25 @@
1
+ require_relative 'access_ac_interaction_test'
2
+ require_relative 'authorization_request_verification_test'
3
+ require_relative 'token_request_ac_verification_test'
4
+ require_relative 'token_use_verification_test'
5
+
6
+ module UDAPSecurityTestKit
7
+ class UDAPClientAccessAuthorizationCode < Inferno::TestGroup
8
+ id :udap_client_access_ac
9
+ title 'Client Access'
10
+ description %(
11
+ During these tests, the client system will access Inferno's simulated
12
+ FHIR server by requesting an access token using UDAP's authorization_code flow
13
+ and making a FHIR request presenting the access token. Inferno will then verify
14
+ that any requests made as a part of obtaining the token were conformant and that
15
+ a token returned from a token request was used on an access request.
16
+ )
17
+
18
+ run_as_group
19
+
20
+ test from: :udap_client_access_ac_interaction
21
+ test from: :udap_client_authorization_request_verification
22
+ test from: :udap_client_token_request_ac_verification
23
+ test from: :udap_client_token_use_verification
24
+ end
25
+ end
@@ -0,0 +1,59 @@
1
+ require_relative '../urls'
2
+ require_relative '../endpoints/mock_udap_server'
3
+ require_relative 'client_descriptions'
4
+
5
+ module UDAPSecurityTestKit
6
+ class UDAPClientAccessAuthorizationCodeInteraction < Inferno::Test
7
+ include URLs
8
+ include ClientWaitDialogDescriptions
9
+
10
+ id :udap_client_access_ac_interaction
11
+ title 'Perform UDAP-secured Access'
12
+ description %(
13
+ During this test, Inferno will wait for the client to access data
14
+ using a UDAP token obtained during an earlier test.
15
+ )
16
+ input :client_id,
17
+ title: 'Client Id',
18
+ type: 'text',
19
+ locked: true,
20
+ description: INPUT_CLIENT_ID_DESCRIPTION_LOCKED
21
+ input :launch_context,
22
+ title: 'Launch Context',
23
+ type: 'textarea',
24
+ optional: true,
25
+ description: INPUT_LAUNCH_CONTEXT_DESCRIPTION
26
+ input :fhir_user_relative_reference,
27
+ title: 'FHIR User Relative Reference',
28
+ type: 'text',
29
+ optional: true,
30
+ description: INPUT_FHIR_USER_RELATIVE_REFERENCE
31
+ input :fhir_read_resources_bundle,
32
+ title: 'Available Resources',
33
+ type: 'textarea',
34
+ optional: true,
35
+ description: INPUT_FHIR_READ_RESOURCES_BUNDLE_DESCRIPTION
36
+ input :echoed_fhir_response,
37
+ title: 'Default FHIR Response',
38
+ type: 'textarea',
39
+ optional: true,
40
+ description: INPUT_ECHOED_FHIR_RESPONSE_DESCRIPTION
41
+
42
+ def client_suite_id
43
+ return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
44
+
45
+ UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
46
+ end
47
+
48
+ run do
49
+ message =
50
+ wait_dialog_authorization_code_access_prefix(client_id, client_fhir_base_url) +
51
+ wait_dialog_access_response_and_continue_suffix(client_id, client_resume_pass_url)
52
+
53
+ wait(
54
+ identifier: client_id,
55
+ message:
56
+ )
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'access_cc_interaction_test'
2
+ require_relative 'token_request_cc_verification_test'
3
+ require_relative 'token_use_verification_test'
4
+
5
+ module UDAPSecurityTestKit
6
+ class UDAPClientAccessClientCredentials < Inferno::TestGroup
7
+ id :udap_client_access_cc
8
+ title 'Client Access'
9
+ description %(
10
+ During these tests, the client system will access Inferno's simulated
11
+ FHIR server by requesting an access token using UDAP's client_credentials flow
12
+ and making a FHIR request presenting the access token. Inferno will then verify
13
+ that any requests made as a part of obtaining the token were conformant and that
14
+ a token returned from a token request was used on an access request.
15
+ )
16
+
17
+ run_as_group
18
+
19
+ test from: :udap_client_access_cc_interaction
20
+ test from: :udap_client_token_request_cc_verification
21
+ test from: :udap_client_token_use_verification
22
+ end
23
+ end
@@ -0,0 +1,49 @@
1
+ require_relative '../urls'
2
+ require_relative '../endpoints/mock_udap_server'
3
+ require_relative 'client_descriptions'
4
+
5
+ module UDAPSecurityTestKit
6
+ class UDAPClientAccessClientCredentialsInteraction < Inferno::Test
7
+ include URLs
8
+ include ClientWaitDialogDescriptions
9
+
10
+ id :udap_client_access_cc_interaction
11
+ title 'Perform UDAP-secured Access'
12
+ description %(
13
+ During this test, Inferno will wait for the client to access data
14
+ using a UDAP token obtained during an earlier test.
15
+ )
16
+ input :client_id,
17
+ title: 'Client Id',
18
+ type: 'text',
19
+ locked: true,
20
+ description: INPUT_CLIENT_ID_DESCRIPTION_LOCKED
21
+ input :fhir_read_resources_bundle,
22
+ title: 'Available Resources',
23
+ type: 'textarea',
24
+ optional: true,
25
+ description: INPUT_FHIR_READ_RESOURCES_BUNDLE_DESCRIPTION
26
+ input :echoed_fhir_response,
27
+ title: 'Default FHIR Response',
28
+ type: 'textarea',
29
+ optional: true,
30
+ description: INPUT_ECHOED_FHIR_RESPONSE_DESCRIPTION
31
+
32
+ def client_suite_id
33
+ return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
34
+
35
+ UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
36
+ end
37
+
38
+ run do
39
+ message =
40
+ wait_dialog_client_credentials_access_prefix(client_id, client_fhir_base_url) +
41
+ wait_dialog_access_response_and_continue_suffix(client_id, client_resume_pass_url)
42
+
43
+ wait(
44
+ identifier: client_id,
45
+ message:
46
+ )
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,83 @@
1
+ require_relative '../tags'
2
+ require_relative '../urls'
3
+ require_relative '../endpoints/mock_udap_server'
4
+ require_relative 'client_descriptions'
5
+
6
+ module UDAPSecurityTestKit
7
+ class UDAPClientAppLaunchAuthorizationRequestVerification < Inferno::Test
8
+ include URLs
9
+
10
+ id :udap_client_authorization_request_verification
11
+ title 'Verify UDAP Authorization Requests'
12
+ description %(
13
+ Check that UDAP authorization requests made are conformant.
14
+ )
15
+
16
+ input :client_id,
17
+ title: 'Client Id',
18
+ type: 'text',
19
+ locked: true,
20
+ description: INPUT_CLIENT_ID_DESCRIPTION_LOCKED
21
+ input :udap_registration_jwt,
22
+ title: 'Registered UDAP Software Statement',
23
+ type: 'textarea',
24
+ locked: 'true',
25
+ description: INPUT_UDAP_REGISTRATION_JWT_DESCRIPTION_LOCKED
26
+
27
+ def client_suite_id
28
+ return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
29
+
30
+ UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
31
+ end
32
+
33
+ run do
34
+ load_tagged_requests(AUTHORIZATION_TAG, UDAP_TAG)
35
+ skip_if requests.blank?, 'No UDAP authorization requests made.'
36
+
37
+ requests.each_with_index do |authorization_request, index|
38
+ auth_code_request_params = MockUDAPServer.authorization_code_request_details(authorization_request)
39
+ check_request_params(auth_code_request_params, index + 1)
40
+ end
41
+
42
+ assert messages.none? { |msg|
43
+ msg[:type] == 'error'
44
+ }, 'Invalid authorization requests detected. See messages for details.'
45
+ end
46
+
47
+ def check_request_params(params, request_num) # rubocop:disable Metrics/CyclomaticComplexity
48
+ if params['response_type'] != 'code'
49
+ add_message('error',
50
+ "Authorization request #{request_num} had an incorrect `response_type`: expected 'code', " \
51
+ "but got '#{params['response_type']}'")
52
+ end
53
+ if params['client_id'] != client_id
54
+ add_message('error',
55
+ "Authorization request #{request_num} had an incorrect `client_id`: expected #{client_id}, " \
56
+ "but got '#{params['client_id']}'")
57
+ end
58
+ registration_body, _registration_header = JWT.decode(udap_registration_jwt, nil, false)
59
+ if params['redirect_uri'].present?
60
+ # must be a registered redirect_uri
61
+ unless registration_body['redirect_uris']&.include?(params['redirect_uri'])
62
+ add_message('error',
63
+ "Authorization request #{request_num} had an invalid `redirect_uri`: expected one of " \
64
+ "'#{registration_body['redirect_uris']&.join(', ')}', but got '#{params['redirect_uri']}'")
65
+ end
66
+ else
67
+ # can only be one registered redirect_uri
68
+ unless registration_body['redirect_uris']&.length == 1
69
+ add_message('error',
70
+ "Authorization request #{request_num} had an invalid `redirect_uri`: expected one of " \
71
+ "'#{registration_body['redirect_uris']&.join(', ')}', but got none")
72
+ end
73
+ end
74
+
75
+ if params['state'].blank?
76
+ add_message('warning',
77
+ "Authorization request #{request_num} is missing the recommended `state` element")
78
+ end
79
+
80
+ nil
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/utils'
4
+
5
+ module UDAPSecurityTestKit
6
+ RE_RUN_REGISTRATION_SUFFIX =
7
+ 'Create a new session and re-run the Client Registration group if you need to change this value.'
8
+ INPUT_CLIENT_ID_DESCRIPTION_LOCKED =
9
+ "The registered Client Id for use in obtaining access tokens. #{RE_RUN_REGISTRATION_SUFFIX}".freeze
10
+ INPUT_UDAP_REGISTRATION_JWT_DESCRIPTION_LOCKED =
11
+ "The software statement JWT provided during UDAP client registration. #{RE_RUN_REGISTRATION_SUFFIX}".freeze
12
+
13
+ INPUT_LAUNCH_CONTEXT_DESCRIPTION =
14
+ 'Launch context details to be included in access token responses, specified as a JSON array. If provided, ' \
15
+ 'the contents will be merged into Inferno\'s token responses.'
16
+ INPUT_FHIR_USER_RELATIVE_REFERENCE =
17
+ 'A FHIR relative reference (<resource type>/<id>) for the FHIR user record to return when the openid ' \
18
+ 'and fhirUser scopes are requested. Include this resource in the **Available Resources** input so ' \
19
+ 'that it can be accessed via FHIR read.'
20
+ INPUT_FHIR_READ_RESOURCES_BUNDLE_DESCRIPTION =
21
+ 'Resources to make available in Inferno\'s simulated FHIR server provided as a FHIR bundle. Each entry ' \
22
+ 'must contain a resource with the id element populated. Each instance present will be available for ' \
23
+ 'retrieval from Inferno at the endpoint: <fhir-base>/<resource type>/<instance id>. These will only ' \
24
+ 'be available through the read interaction.'
25
+ INPUT_ECHOED_FHIR_RESPONSE_DESCRIPTION =
26
+ 'JSON representation of a default FHIR resource for Inferno to echo when a request is made to the ' \
27
+ 'simulated FHIR server. Reads targetting resources in the **Available Resources** input will return ' \
28
+ 'that resource instead of this. Otherwise, the content here will be echoed back exactly and no check ' \
29
+ 'will be made that it is appropriate for the request made. If nothing is provided, an OperationOutcome ' \
30
+ 'indicating nothing to echo will be returned.'
31
+
32
+ module ClientWaitDialogDescriptions
33
+ def wait_dialog_client_credentials_access_prefix(client_id, fhir_base_url)
34
+ <<~PREFIX
35
+ **Access**
36
+
37
+ Use the registered client id (#{client_id}) to obtain an access
38
+ token using the [UDAP B2B client credentials flow](https://hl7.org/fhir/us/udap-security/STU1/b2b.html)
39
+ and use that token to access a FHIR endpoint under the simulated server's base URL
40
+
41
+ `#{fhir_base_url}`
42
+ PREFIX
43
+ end
44
+
45
+ def wait_dialog_authorization_code_access_prefix(client_id, fhir_base_url)
46
+ <<~PREFIX
47
+ **Access**
48
+
49
+ Use the registered client id (#{client_id}) to obtain an access
50
+ token using the UDAP [consumer-facing](https://hl7.org/fhir/us/udap-security/STU1/consumer.html)
51
+ or [B2B authorization code flow](https://hl7.org/fhir/us/udap-security/STU1/b2b.html)
52
+ and use that token to access a FHIR endpoint under the simulated server's base URL
53
+
54
+ `#{fhir_base_url}`
55
+ PREFIX
56
+ end
57
+
58
+ def wait_dialog_access_response_and_continue_suffix(client_id, resume_pass_url)
59
+ <<~SUFFIX
60
+ Inferno will respond to requests with either:
61
+ - A resource from the Bundle in the **Available Resources** input if the request is a read matching
62
+ a resource type and id found in the Bundle.
63
+ - Otherwise, the contents of the **Default FHIR Response** if provided.
64
+ - Otherwise, an OperationOutcome indicating nothing to echo.
65
+
66
+ [Click here](#{resume_pass_url}?token=#{client_id}) once you performed the data access.
67
+ SUFFIX
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../tags'
4
+
5
+ module UDAPSecurityTestKit
6
+ module UDAPClientOptions
7
+ module_function
8
+
9
+ UDAP_AUTHORIZATION_CODE = "#{UDAP_TAG},#{AUTHORIZATION_CODE_TAG}".freeze
10
+ UDAP_CLIENT_CREDENTIALS = "#{UDAP_TAG},#{CLIENT_CREDENTIALS_TAG}".freeze
11
+
12
+ def oauth_flow(suite_options)
13
+ if suite_options[:client_type].include?(AUTHORIZATION_CODE_TAG)
14
+ AUTHORIZATION_CODE_TAG
15
+ elsif suite_options[:client_type].include?(CLIENT_CREDENTIALS_TAG)
16
+ CLIENT_CREDENTIALS_TAG
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ {
2
+ "keys": [
3
+ {
4
+ "kty": "RSA",
5
+ "use": "sig",
6
+ "key_ops": [
7
+ "verify"
8
+ ],
9
+ "alg": "RS256",
10
+ "kid": "618e76fc7eb7c3401c7fd47b9af5e94d",
11
+ "n": "uoYzdYZQDp7yHTfY4XL1PFCFPfijG1rP2dNgMmsqKbm9qpMjiUuXCpdoOXWE838pcu3Ahi6L3dGgGS3Aeys3YrnF6h9uRTD1DHf-UWlOq4iPCzk26a-bBuyVZDVqS7Y5HsyF7i_UvYV1W0EaHSpAF911Vo_W_FVyiSkAZP1yN0ECbbjnSqxhaa44SGw90oh6XnyNBLu7a11aLCURkaRJOKXHj_vf8UyaD-IA9YkvM80St7QF6u6LRVYmCShbpfWRmejh_Swf4zdqcPl-duUsZE-OBysoTq92FaXr605igM30Rdkx8hs-HTwZtjn5hB3XNTiG2Wka1cUXKNyH59u8Yw",
12
+ "e": "AQAB"
13
+ },
14
+ {
15
+ "kty": "RSA",
16
+ "use": "sig",
17
+ "key_ops": [
18
+ "sign"
19
+ ],
20
+ "alg": "RS256",
21
+ "kid": "618e76fc7eb7c3401c7fd47b9af5e94d",
22
+ "d": "k2SxDVHRuXwIvuX-0EjTWZIXeF0eJuOgE_VgsvbUHpzUMBKNplTBSnFSvvUK1o_J5TPTSzVE-UhJRxxMWghQgAdlShkEPlDtk6jOou6gaBRFVQ0lQ4ys6M_TTZiYIrQgdyIPQ6Uwa4MmtbHAPQPCGhm6O2j27fdnxtNLqIJO2zF9OzF4VP5_v63bbMfwbKECvB_bbcGLmJrq2ClI1iTOw-GawQ3I8sfwf52D3mb-qDJyQwtomNxf7EhvmGBC9u_8JVY48qsCCYWr2KAauID02WtCVepxM4ltKOLJb6BL5QpKmJ8svYLWjSZvNJSdk-EoB9grqechyDOIa2DuU9g-QQ",
23
+ "n": "uoYzdYZQDp7yHTfY4XL1PFCFPfijG1rP2dNgMmsqKbm9qpMjiUuXCpdoOXWE838pcu3Ahi6L3dGgGS3Aeys3YrnF6h9uRTD1DHf-UWlOq4iPCzk26a-bBuyVZDVqS7Y5HsyF7i_UvYV1W0EaHSpAF911Vo_W_FVyiSkAZP1yN0ECbbjnSqxhaa44SGw90oh6XnyNBLu7a11aLCURkaRJOKXHj_vf8UyaD-IA9YkvM80St7QF6u6LRVYmCShbpfWRmejh_Swf4zdqcPl-duUsZE-OBysoTq92FaXr605igM30Rdkx8hs-HTwZtjn5hB3XNTiG2Wka1cUXKNyH59u8Yw",
24
+ "e": "AQAB",
25
+ "p": "x3syoDXXDJne_-aSeHMm3muQ1KfIGewMhklvGNZ6LVTIJWUE5UYiGshABmgn86GfS-uZc_oUo3WcXiCLUlZSBqZFTIyXgy9y979VWA0whPCYcVSLzSGOiUtv3Ys4VGYCmGSuJnyLDp0CNN63Gf_iGpjCRbaPCPdRnyBgfSEvETs",
26
+ "q": "718z8hC_AQXrhQxvTsCEv7FXSX0Ev10Tjdq94DcsD91g34KTbrF9K9wtPfBUQaUx5Z2rMOTdLxbtHKvP-YQ4YMoQioA9qsclcnwdvKoRcRWim9oBnLa3Iuqttdwc9U2FWEQ4wqv16rsQw7URU4qlYkiVjrHvRJb8Nx_AJsA8Tvk",
27
+ "dp": "waDGDVj1exfIq-ClgCFWM0N5-9E4nGDR729MVXGqemH3PMUHsX0YEaMa8p0bWpMhStJPy5GNgvTgaUVxtuRvDmFKlvlJAF-IWw7vyl5TIFdhwW_tm5nc_0uoNAW1EcdK8Z2YpWbym6avw54DYUtNr79jo8OGp49ZPPpybkNNqo0",
28
+ "dq": "ge3mL02Br9d7yKNAQ7niFH75Ry1yB0FJXOVPzUWFSDM84vVoe1wh-k2vzQAHa_50ABO-GXMQz_-cwsRLxj9LrtXfdp43Wtxv6h2OspqJjx1UP05tM5hF_dDua1lH6qqiZ4_YU2qtuDTD28cL2ZHXRWrqqyLQIiXmTzGPxjjwQ1k",
29
+ "qi": "n3fMznRlCCwuoCq0kv4tL0cravVhcg33rA-CQXlIxgqj3h1yiBJ4p76pHceV6tvYsPyX3YsUKNOBP9HcxuHK3I3kFqMN5Y4C4r8dTE4fbJK9QT_guo_k_GKijNp0NDNCCeKxU78SRYvA527VY-3Z6TUC_K6O-5Pd6aPJN1QDtoA"
30
+ }
31
+ ]
32
+ }
@@ -0,0 +1,27 @@
1
+ require 'jwt'
2
+
3
+ module UDAPSecurityTestKit
4
+ class OIDCJWKS
5
+ class << self
6
+ def jwks_json
7
+ @jwks_json ||=
8
+ JSON.pretty_generate(
9
+ { keys: jwks.export[:keys].select { |key| key[:key_ops]&.include?('verify') } }
10
+ )
11
+ end
12
+
13
+ def default_jwks_path
14
+ @default_jwks_path ||= File.join(__dir__, 'oidc_jwks.json')
15
+ end
16
+
17
+ def jwks_path
18
+ @jwks_path ||=
19
+ ENV.fetch('SIMULATED_OIDC_JWKS_PATH', default_jwks_path)
20
+ end
21
+
22
+ def jwks
23
+ @jwks ||= JWT::JWK::Set.new(JSON.parse(File.read(jwks_path)))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'registration_interaction_test'
2
+ require_relative 'registration_ac_verification_test'
3
+
4
+ module UDAPSecurityTestKit
5
+ class UDAPClientRegistrationAuthorizationCode < Inferno::TestGroup
6
+ id :udap_client_registration_ac
7
+ title 'Client Registration'
8
+ description %(
9
+ During these tests, the client system will dynamically register with Inferno's
10
+ simulated UDAP Server to use the authorization_code flow. At any time, the client
11
+ may perform UDAP discovery on the simulated Inferno UDAP server.
12
+ )
13
+ run_as_group
14
+
15
+ test from: :udap_client_registration_interaction
16
+ test from: :udap_client_registration_ac_verification
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ require_relative '../tags'
2
+ require_relative '../urls'
3
+ require_relative '../endpoints/mock_udap_server'
4
+ require_relative 'registration_request_verification'
5
+
6
+ module UDAPSecurityTestKit
7
+ class UDAPClientRegistrationAuthorizationCodeVerification < Inferno::Test
8
+ include URLs
9
+ include RegistrationRequestVerification
10
+
11
+ id :udap_client_registration_ac_verification
12
+ title 'Verify UDAP Authorization Code Registration'
13
+ description %(
14
+ During this test, Inferno will verify that the client's UDAP
15
+ registration request is conformant.
16
+ )
17
+ input :udap_client_uri
18
+ output :udap_registration_jwt
19
+
20
+ def client_suite_id
21
+ return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
22
+
23
+ UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
24
+ end
25
+
26
+ run do
27
+ client_registration_requests = load_registration_requests_for_client_uri(udap_client_uri)
28
+ skip_if client_registration_requests.empty?,
29
+ "No UDAP Registration Requests made for client uri '#{udap_client_uri}'."
30
+
31
+ verify_registration_request(AUTHORIZATION_CODE_TAG, client_registration_requests.last) # most recent if several
32
+
33
+ assert messages.none? { |msg|
34
+ msg[:type] == 'error'
35
+ }, 'Invalid registration request. See messages for details.'
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'registration_interaction_test'
2
+ require_relative 'registration_cc_verification_test'
3
+
4
+ module UDAPSecurityTestKit
5
+ class UDAPClientRegistrationClientCredentials < Inferno::TestGroup
6
+ id :udap_client_registration_cc
7
+ title 'Client Registration'
8
+ description %(
9
+ During these tests, the client system will dynamically register with Inferno's
10
+ simulated UDAP Server to use the client_credentials flow. At any time, the client
11
+ may perform UDAP discovery on the simulated Inferno UDAP server.
12
+ )
13
+ run_as_group
14
+
15
+ test from: :udap_client_registration_interaction
16
+ test from: :udap_client_registration_cc_verification
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ require_relative '../tags'
2
+ require_relative '../urls'
3
+ require_relative '../endpoints/mock_udap_server'
4
+ require_relative 'registration_request_verification'
5
+
6
+ module UDAPSecurityTestKit
7
+ class UDAPClientRegistrationClientCredentialsVerification < Inferno::Test
8
+ include URLs
9
+ include RegistrationRequestVerification
10
+
11
+ id :udap_client_registration_cc_verification
12
+ title 'Verify UDAP Client Credentials Registration'
13
+ description %(
14
+ During this test, Inferno will verify that the client's UDAP
15
+ registration request is conformant.
16
+ )
17
+ input :udap_client_uri
18
+ output :udap_registration_jwt
19
+
20
+ def client_suite_id
21
+ return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
22
+
23
+ UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
24
+ end
25
+
26
+ run do
27
+ client_registration_requests = load_registration_requests_for_client_uri(udap_client_uri)
28
+ skip_if client_registration_requests.empty?,
29
+ "No UDAP Registration Requests made for client uri '#{udap_client_uri}'."
30
+
31
+ verify_registration_request(CLIENT_CREDENTIALS_TAG, client_registration_requests.last) # most recent if several
32
+
33
+ assert messages.none? { |msg|
34
+ msg[:type] == 'error'
35
+ }, 'Invalid registration request. See messages for details.'
36
+ end
37
+ end
38
+ end
@@ -13,14 +13,21 @@ module UDAPSecurityTestKit
13
13
  using UDAP dynamic registration.
14
14
  )
15
15
  input :udap_client_uri,
16
- optional: false
16
+ title: 'UDAP Client URI',
17
+ type: 'text',
18
+ description: %(
19
+ The UDAP Client URI that will be used to register with Inferno's simulated UDAP server.
20
+ )
17
21
 
18
22
  output :client_id
19
23
 
20
- run do
21
- omit_if udap_client_uri.blank?, # for re-use: mark the udap_client_uri input as optional when importing to enable
22
- 'Not configured for UDAP authentication.'
24
+ def client_suite_id
25
+ return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
23
26
 
27
+ UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
28
+ end
29
+
30
+ run do
24
31
  generated_client_id = MockUDAPServer.client_uri_to_client_id(udap_client_uri)
25
32
  output client_id: generated_client_id
26
33