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.
- checksums.yaml +4 -4
- data/config/presets/UDAP_RunServerAgainstClient.json.erb +4 -4
- data/lib/udap_security_test_kit/client_suite/access_ac_group.rb +25 -0
- data/lib/udap_security_test_kit/client_suite/access_ac_interaction_test.rb +59 -0
- data/lib/udap_security_test_kit/client_suite/access_cc_group.rb +23 -0
- data/lib/udap_security_test_kit/client_suite/access_cc_interaction_test.rb +49 -0
- data/lib/udap_security_test_kit/client_suite/authorization_request_verification_test.rb +83 -0
- data/lib/udap_security_test_kit/client_suite/client_descriptions.rb +70 -0
- data/lib/udap_security_test_kit/client_suite/client_options.rb +20 -0
- data/lib/udap_security_test_kit/client_suite/oidc_jwks.json +32 -0
- data/lib/udap_security_test_kit/client_suite/oidc_jwks.rb +27 -0
- data/lib/udap_security_test_kit/client_suite/registration_ac_group.rb +18 -0
- data/lib/udap_security_test_kit/client_suite/registration_ac_verification_test.rb +38 -0
- data/lib/udap_security_test_kit/client_suite/registration_cc_group.rb +18 -0
- data/lib/udap_security_test_kit/client_suite/registration_cc_verification_test.rb +38 -0
- data/lib/udap_security_test_kit/client_suite/{client_registration_interaction_test.rb → registration_interaction_test.rb} +11 -4
- data/lib/udap_security_test_kit/client_suite/{client_registration_verification_test.rb → registration_request_verification.rb} +38 -40
- data/lib/udap_security_test_kit/client_suite/token_request_ac_verification_test.rb +49 -0
- data/lib/udap_security_test_kit/client_suite/token_request_cc_verification_test.rb +49 -0
- data/lib/udap_security_test_kit/client_suite/{client_token_request_verification_test.rb → token_request_verification.rb} +91 -46
- data/lib/udap_security_test_kit/client_suite/{client_token_use_verification_test.rb → token_use_verification_test.rb} +0 -3
- data/lib/udap_security_test_kit/client_suite.rb +46 -17
- data/lib/udap_security_test_kit/docs/udap_client_suite_description.md +74 -31
- data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder_endpoint.rb +96 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/authorization_endpoint.rb +28 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration_endpoint.rb +31 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/token_endpoint.rb +56 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_authorization_response_creation.rb +63 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_registration_response_creation.rb +28 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_token_response_creation.rb +218 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server.rb +112 -31
- data/lib/udap_security_test_kit/metadata.rb +1 -1
- data/lib/udap_security_test_kit/tags.rb +4 -0
- data/lib/udap_security_test_kit/urls.rb +15 -8
- data/lib/udap_security_test_kit/version.rb +2 -2
- metadata +28 -12
- data/lib/udap_security_test_kit/client_suite/client_access_group.rb +0 -22
- data/lib/udap_security_test_kit/client_suite/client_access_interaction_test.rb +0 -53
- data/lib/udap_security_test_kit/client_suite/client_registration_group.rb +0 -26
- data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder.rb +0 -52
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration.rb +0 -57
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93cc168a29a41ac4c22bdcb8a4124aa85c370d3a0ae8ac1851090555ab3a2e8b
|
4
|
+
data.tar.gz: d31eee97042a453fe3ccf6c72c1421309ff4d367eb87ff6d2f3a63a96529041d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
21
|
-
|
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
|
|