From c1301a013422e40c26f957b2626394964882ffff Mon Sep 17 00:00:00 2001 From: Alisa Wallace <140003092+alisawallace@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:43:34 -0800 Subject: [PATCH] FI-3175 Allow testers to specify new or updated client registration attempt (#17) * Create client_registration_status input in test, groups * Update registration success test to check input for expected response code * Update registration success spec tests to check for 200 vs 201 response type * Update input instructions and test descriptions * Accept 200 or 201 response status for successful updated registration requests * Update input instructions and test descriptions --- .../authorization_code_group.rb | 4 ++ .../client_credentials_group.rb | 4 ++ .../dynamic_client_registration_group.rb | 34 ++++++++--- .../registration_success_test.rb | 16 ++++- .../registration_success_test_spec.rb | 58 +++++++++++++++---- 5 files changed, 97 insertions(+), 19 deletions(-) diff --git a/lib/udap_security_test_kit/authorization_code_group.rb b/lib/udap_security_test_kit/authorization_code_group.rb index 979dcc9..507220a 100644 --- a/lib/udap_security_test_kit/authorization_code_group.rb +++ b/lib/udap_security_test_kit/authorization_code_group.rb @@ -49,6 +49,9 @@ class AuthorizationCodeGroup < Inferno::TestGroup default: 'authorization_code', locked: true }, + udap_client_registration_status: { + name: :udap_auth_code_flow_client_registration_status + }, udap_client_cert_pem: { name: :udap_auth_code_flow_client_cert_pem, title: 'Authorization Code Client Certificate(s) (PEM Format)' @@ -90,6 +93,7 @@ class AuthorizationCodeGroup < Inferno::TestGroup } do input_order :udap_registration_endpoint, :udap_auth_code_flow_registration_grant_type, + :udap_auth_code_flow_client_registration_status, :udap_auth_code_flow_client_cert_pem, :udap_auth_code_flow_client_private_key, :udap_auth_code_flow_cert_iss, diff --git a/lib/udap_security_test_kit/client_credentials_group.rb b/lib/udap_security_test_kit/client_credentials_group.rb index 723e60b..74a07f9 100644 --- a/lib/udap_security_test_kit/client_credentials_group.rb +++ b/lib/udap_security_test_kit/client_credentials_group.rb @@ -51,6 +51,9 @@ class ClientCredentialsGroup < Inferno::TestGroup default: 'client_credentials', locked: true }, + udap_client_registration_status: { + name: :udap_client_credentials_flow_client_registration_status + }, udap_client_cert_pem: { name: :udap_client_credentials_flow_client_cert_pem, title: 'Client Credentials Client Certificate(s) (PEM Format)' @@ -92,6 +95,7 @@ class ClientCredentialsGroup < Inferno::TestGroup } do input_order :udap_registration_endpoint, :udap_client_credentials_flow_registration_grant_type, + :udap_client_credentials_flow_client_registration_status, :udap_client_credentials_flow_client_cert_pem, :udap_client_credentials_flow_client_private_key, :udap_cert_iss_client_creds_flow, diff --git a/lib/udap_security_test_kit/dynamic_client_registration_group.rb b/lib/udap_security_test_kit/dynamic_client_registration_group.rb index c20bc11..66cd828 100644 --- a/lib/udap_security_test_kit/dynamic_client_registration_group.rb +++ b/lib/udap_security_test_kit/dynamic_client_registration_group.rb @@ -19,13 +19,12 @@ def self.dynamic_client_registration_input_instructions establish a trust chain. Cancelling a UDAP client's registration is not a required server capability and as such the Inferno client has no - way of resetting state on the authorization server after a successful registration attempt. Testers wishing to - run the Dynamic Client Registration tests more than once must do one of the following: - - Remove the Inferno test client's registration out-of-band before re-running tests, to register the original - client URI anew - - Specifiy a different client URI as the issuer input (if the client cert has more than one Subject Alternative - Name (SAN) URI entry), to register a different logical client with the original certificate - - Provide a different client certificate and its associated URI to register a new logical client + way of resetting state on the authorization server after a successful registration attempt. If a given + certificate and issuer URI identity combination has already been registered with the authorization server, testers + whose systems support registration modifications + may select the "Update Registration" option under Client Registration Status. This option will accept either a + `200 OK` or `201 Created` return status. Registration attempts for a new client may only return `201 Created`, + per the [IG](https://hl7.org/fhir/us/udap-security/STU1/registration.html#request-body). ) end @@ -57,6 +56,27 @@ def self.dynamic_client_registration_input_instructions ] } + input :udap_client_registration_status, + title: 'Client Registration Status', + description: %( + If the client's iss and certificate combination has already been registered with the authorization server + prior to this test run, select 'Update'. + ), + type: 'radio', + options: { + list_options: [ + { + label: 'New Registration (201 Response Code Expected)', + value: 'new' + }, + { + label: 'Update Registration (200 or 201 Response Code Expected)', + value: 'update' + } + ] + }, + default: 'new' + input :udap_client_cert_pem, title: 'X.509 Client Certificate(s) (PEM Format)', description: %( diff --git a/lib/udap_security_test_kit/registration_success_test.rb b/lib/udap_security_test_kit/registration_success_test.rb index d17cd26..9ca60e5 100644 --- a/lib/udap_security_test_kit/registration_success_test.rb +++ b/lib/udap_security_test_kit/registration_success_test.rb @@ -13,6 +13,14 @@ class RegistrationSuccessTest < Inferno::Test The [UDAP IG Section 3.2.3](https://hl7.org/fhir/us/udap-security/STU1/registration.html#request-body) states: > If a new registration is successful, the Authorization Server SHALL return a registration response with a 201 > Created HTTP response code as per Section 5.1 of UDAP Dynamic Client Registration + + If the tester indicated this registration attempt represents a modification of an existing registration entry, + the [UDAP IG Section 3.4](https://hl7.org/fhir/us/udap-security/STU1/registration.html#modifying-and-cancelling-registrations) + states: + > If the Authorization Server returns the same client_id in the registration response for a modification request, + > it SHOULD also return a 200 OK HTTP response code. + + In this case, the test will require either a 201 or 200 response code to pass. ) input :udap_client_cert_pem @@ -20,6 +28,7 @@ class RegistrationSuccessTest < Inferno::Test input :udap_cert_iss input :udap_registration_endpoint + input :udap_client_registration_status input :udap_jwt_signing_alg input :udap_registration_requested_scope input :udap_registration_grant_type @@ -60,7 +69,12 @@ class RegistrationSuccessTest < Inferno::Test post(udap_registration_endpoint, body: reg_body, headers: reg_headers) - assert_response_status(201) + if udap_client_registration_status == 'new' + assert_response_status(201) + elsif udap_client_registration_status == 'update' + assert_response_status([200, 201]) + end + assert_valid_json(response[:body]) output udap_registration_response: response[:body] end diff --git a/spec/udap_security_test_kit/registration_success_test_spec.rb b/spec/udap_security_test_kit/registration_success_test_spec.rb index 4743ad8..ffbdee1 100644 --- a/spec/udap_security_test_kit/registration_success_test_spec.rb +++ b/spec/udap_security_test_kit/registration_success_test_spec.rb @@ -21,6 +21,7 @@ let(:udap_registration_requested_scope) { 'system/*' } let(:udap_registration_grant_type) { 'client_credentials' } let(:udap_registration_certifications) { '' } + let(:udap_client_registration_status) { 'new' } let(:input) do { udap_client_cert_pem:, @@ -30,7 +31,8 @@ udap_jwt_signing_alg:, udap_registration_requested_scope:, udap_registration_grant_type:, - udap_registration_certifications: + udap_registration_certifications:, + udap_client_registration_status: } end @@ -48,21 +50,55 @@ def run(runnable, inputs = {}) Inferno::TestRunner.new(test_session:, test_run:).run(runnable) end - it 'fails if response status is not 201' do - stub_request(:post, udap_registration_endpoint) - .to_return(status: 400, body: {}.to_json) + context 'when new client is being registered' do + it 'fails if response status is not 201' do + stub_request(:post, udap_registration_endpoint) + .to_return(status: 200, body: {}.to_json) - result = run(runnable, input) + result = run(runnable, input) - expect(result.result).to eq('fail') + expect(result.result).to eq('fail') + end + + it 'passes when response status is 201' do + stub_request(:post, udap_registration_endpoint) + .to_return(status: 201, body: {}.to_json) + + result = run(runnable, input) + + expect(result.result).to eq('pass') + end end - it 'passes when response status is 201' do - stub_request(:post, udap_registration_endpoint) - .to_return(status: 201, body: {}.to_json) + context 'when existing client is updating its registration data' do + it 'fails if response status is not 200 or 201' do + stub_request(:post, udap_registration_endpoint) + .to_return(status: 401, body: {}.to_json) + + input[:udap_client_registration_status] = 'update' + result = run(runnable, input) + + expect(result.result).to eq('fail') + end + + it 'passes when response status is 200' do + stub_request(:post, udap_registration_endpoint) + .to_return(status: 200, body: {}.to_json) - result = run(runnable, input) + input[:udap_client_registration_status] = 'update' + result = run(runnable, input) + + expect(result.result).to eq('pass') + end - expect(result.result).to eq('pass') + it 'passes when response status is 201' do + stub_request(:post, udap_registration_endpoint) + .to_return(status: 201, body: {}.to_json) + + input[:udap_client_registration_status] = 'update' + result = run(runnable, input) + + expect(result.result).to eq('pass') + end end end