Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FI-2247 backend services migration #59

Merged
merged 41 commits into from
Jan 31, 2024

Conversation

alisawallace
Copy link
Contributor

@alisawallace alisawallace commented Dec 12, 2023

Summary

Migrating the Bulk Data Authorization tests into the SMART App Launch test kit. Bulk Data test files were copied from the ONC (g)(10) Certification Test Kits, including this PR from an external contributor, and now live under a Backend Services group for STU2.

Updates From First Round of Feedback

  • Test Structure
    • Backend services test group is now located after EHR launch and before Token Introspection group
    • Backend services test group includes SMART on FHIR Discovery tests as required by the IG, which will provide the token endpoint input
    • Individual tests now live in their own files
    • Eliminated redundant behavior between ClientAssertionBuilder and AuthenticationRequestBuilder
      • Renamed AuthenticationRequestBuilder to BackendServicesAuthorizationRequestBuilder and had it use ClientAssertionBuilder
  • Inputs
    • Backend services tests now reuse an existing input for encryption method from standalone launch tests using confidential asymmetric client authorization, rather than having a separate, redundant input
    • Inferno Reference Server STU2 preset updated to include backend_services_client_id
      • Value was taken from ref server preset used on inferno.healthit.gov for bulk data authorization
      • All other inputs are either covered by default values or pre-existing presets
  • Naming, Descriptions, & Requirements
    • Replaced all references to (g)(10) and/or bulk data with backend services in test names, inputs, and descriptions
    • Ensured all tests adhere to backend services STU 2.0.0 IG requirements and do not contain requirements specific to (g)(10) or bulk data

Testing Guidance

  1. Run the test kit locally per the README instructions
  2. Navigate to http://localhost:4567 in a web browser
  3. Select SMART App Launch STU2
  4. Select Inferno Reference Server as preset
  5. Select Group 3 Backend Services in the left-side menu, then Run Tests
  6. Run the tests - all tests should pass

require 'json/jwt'

module SMARTAppLaunch
class AuthorizationRequestBuilder
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would hope that this and ClientAssertionBuilder could be combined, as backend services authorization is based on the asymmetric client credentials authorization method. If we need a specific class to handle this for backend services, the class name should reflect that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClientAssertionBuilder and AuthorizationRequestBuilder address different scopes and use cases for the tests that use them. AuthorizationRequestBuilder, used for Backend Services, is too broad for use in standalone/EHR tests, and ClientAssertionBuilder is too narrow for Backend Services (tests would have duplicated code across them that AuthRequestBuilder addresses).

So I opted to keep both classes but had AuthRequestBuilder use ClientAssertionBuilder in building the requests, so there isn't redundant behavior between the two classes. I also renamed AuthRequestBuilder to be specific to Backend Services, since those are the only tests that use it.

Copy link
Contributor

@Jammjammjamm Jammjammjamm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this being merged into main?

The preset needs to be updated. This functionality needs unit tests.

@@ -222,5 +223,7 @@ class SMARTSTU2Suite < Inferno::TestSuite

group from: :smart_token_introspection

group from: :smart_backend_services
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should go before the token introspection tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated so backend services shows up before token introspection tests.

test from: :tls_version_test do
title 'Authorization service token endpoint secured by transport layer security'
description <<~DESCRIPTION
[§170.315(g)(10) Test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't contain anything g10 specific. Is this a backend services requirement or just a g10 requirement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a backend services requirement too, so I left it, but updated all naming so there isn't anything g10 specific.

The OAuth 2.0 Token Endpoint used by the Backend Services specification to provide bearer tokens.
DESCRIPTION
input :bulk_client_id,
title: 'Bulk Data Client ID',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These titles should be updated as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All titles/inputs/descriptions have been updated so they no longer refer to bulk data.

)
end

test do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests should all be moved into their own files and given good ids.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each test was moved into its own file and given a descriptive ID.


id :smart_backend_services

input :bulk_token_endpoint,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input ids should be updated to reflect that these are backend services inputs not bulk data inputs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All inputs were updated.

@alisawallace
Copy link
Contributor Author

Why isn't this being merged into main?

The preset needs to be updated. This functionality needs unit tests.

@Jammjammjamm I mentioned in the PR summary that I split the original 2247 ticket into two tickets, and updated the definition of done for this one to be that the bulk data/backend services tests properly show up in the SMART test kit and that the tests pass with the reference server. Everything else beyond this (renaming, refactoring, unit tests, etc) will be addressed in the next ticket, 2369, that is still in progress. I set up an interim working branch to hold everything until the effort is completed. Rob and I used this approach for token introspection since we kept having to break down the tasking further, and it was helpful for me get PR feedback before things were ready for main. Maybe backend services will be more straightforward and this won't end up being as useful, but that was my thought process.

Thank you for the feedback though - it'll be very helpful as I get started on the next ticket. I will work through each one and reach out if I have questions.

@Jammjammjamm
Copy link
Contributor

I don't think this work justifies a separate non-ephemeral branch. Rather than merging this into a separate branch, this should be a draft PR targeting main, and FI-2369, to the extent that it lines up with my comments, should be incorporated into this branch.

Any work on enhancements needed to expand the functionality and make it more generic that were envisioned as part of FI-2369 would make sense as a separate ticket, but not the sort of organizational changes I'm requesting here. I'd be happy to discuss this further on slack if you like.

@alisawallace alisawallace changed the base branch from backend-services to main December 19, 2023 16:51
@alisawallace alisawallace marked this pull request as draft December 19, 2023 16:52
@alisawallace alisawallace removed the request for review from emichaud998 December 19, 2023 16:53
@alisawallace
Copy link
Contributor Author

@Jammjammjamm I have addressed all of your PR feedback at this point except unit tests, so the PR is still a draft but is ready for re-review. I'll get started on unit tests while waiting for feedback.

@alisawallace alisawallace marked this pull request as ready for review January 17, 2024 21:34
@@ -41,7 +41,7 @@
"value": null
},
{
"name": "client_auth_encryption_method",
"name": "asymm_conf_client_encryption_method",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should change this input name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name has been reverted back to client_auth_encryption_method

id :smart_backend_services_auth_request_success
title 'Authorization request succeeds when supplied correct information'
description <<~DESCRIPTION
[The SMART Backend Services IG STU 2.0.0](https://hl7.org/fhir/smart-app-launch/STU2/backend-services.html#issue-access-token)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no SMART Backend Services IG. There's only the SMART App Launch IG.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the link titles to be "SMART App Launch 2.0.0 specification for Backend Services" instead.

@@ -220,6 +221,23 @@ class SMARTSTU2Suite < Inferno::TestSuite
}
end

group do
title 'Backend Services'
id :smart_full_backend_services
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awkward. I think the subgroup should be something like "Backend Services Authorization" to avoid this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed main suite group to smart_backend_services and the subgroup to smart_backend_services_authorization

@@ -1,5 +1,6 @@
require_relative 'client_assertion_builder'
require_relative 'token_exchange_test'
require_relative 'backend_services_group'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this test require the backend services group?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't, it was accidentally leftover from when I was testing some inputs and has been removed now.

Inferno::TestRunner.new(test_session:, test_run:).run(runnable)
end

describe '[Invalid grant_type] test' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for these specs to be here rather than in a spec for the specific tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied and pasted the specs from the ones already present in the (g)(10) test kit, found in a single file here, and just made some minor naming updates for them to pass. If they need to be refactored and separated out to separate files I can do so.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, they should each have their own file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spec tests have been refactored so each backend services test/class has its own spec file.


expect {
build_and_decode_jwt(encryption_method, kid)
}.to raise_error(RuntimeError)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raising generic errors is something we try very hard to avoid in tests. What are the circumstances where this error could appear?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I included the underlying exception, found here, to support the new functionality of making the kid of the JWKS a user input (from this PR). I found that when I entered a valid kid but accidentally specified the wrong algorithm I got a somewhat cryptic error "undefined method signing_key for nil:NilClass" in client_assertion_builder.rb. What was happening was that the signing_key method of that class was returning a null object as a result of there being no match for the specified kid and algorithm input when searching the key set. I catch an exception in this case to communicate that to the user.

Maybe this doesn't warrant the custom error message? Or if it does, there's a better way to structure the exception? I'm not very familiar with this in Ruby.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this error would be the result of bad inputs? If so, it should be an Inferno::Exceptions::AssertionException. If this would be caused by private_key being nil, the intention would be clearer by having a guard against it being nil that raises this exception rather than rescuing NoMethodError.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Yes, this is a result of bad inputs. I've updated this to check for a nil private_key and to raise Inferno::Exceptions::AssertionException instead.

end

def client_assertion
@client_assertion ||= ClientAssertionBuilder.build(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

require_relative 'client_assertion_builder'

module SMARTAppLaunch
class BackendServicesAuthorizationRequestBuilder
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these file names should be the lower snake case version of the class name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated all the file names (though some of them are pretty long now, hopefully that's okay).

@alisawallace
Copy link
Contributor Author

@Jammjammjamm I addressed all of your comments and am ready for re-review. The following items in particular could use your input:

  • If I need to put all the spec tests in separate files or can leave them grouped
  • Whether my approach of raising an exception to clarify bad kid input is warranted/could be improved.

Thanks!

@alisawallace
Copy link
Contributor Author

@Jammjammjamm I addressed the remaining comments and am ready for re-review:

  • Changed the except raised for bad kid input to your suggestion
  • Refactored all spec tests for backend services so each test/class has their own file

Confirmed that all spec tests are passing and tests in the browser pass with reference server preset. Thanks!

@alisawallace alisawallace merged commit c4340a8 into main Jan 31, 2024
2 checks passed
@alisawallace alisawallace deleted the FI-2247-backend-services-migration branch February 1, 2024 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants