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

[feat][broker] Update AuthenticationProvider to simplify HTTP Authn #19197

Merged

Conversation

michaeljmarshall
Copy link
Member

@michaeljmarshall michaeljmarshall commented Jan 11, 2023

PIP: #12105

Motivation

This is the first of several PRs to implement PIP 97.

This PR seeks to start to solve the fact that the AuthenticationState class currently authenticates authData twice instead of just once. This change is important to make before we are able to utilize the async methods introduced in #12104.

Historical context: #14044 introduced the AuthenticationProvider#newHttpAuthState method. The only use case for this method in the pulsar code base is to let custom providers specify the AuthenticationDataSource on http request attributes. The primary problem with that implementation is that the AuthenticationState class currently involves authenticating the authData passed in to the newHttpAuthState. As such, this code is sub-optimal, and creates a confusing flow.

I propose we deprecate the newHttpAuthState method and instead start using the authenticateHttpRequestAsync and authenticateHttpRequest methods to allow custom implementations to configure the desired AuthenticationDataSource on the request attributes.

In order to simplify the diff for reviewers, this PR uses the deprecated AuthenticationProvider#authenticateHttpRequest method. I plan to follow up and switch to use the AuthenticationProvider#authenticateHttpRequestAsync method.

Note that these changes are completely backwards compatible. The only risk is to users that have custom code loaded into the broker that calls the AuthenticationProvider#authenticateHttpRequest method.

Modifications

  • Deprecate AuthenticationService#authenticateHttpRequest(HttpServletRequest request, AuthenticationDataSource authData). It is no longer used.
  • Deprecate AuthenticationProvider#newHttpAuthState(HttpServletRequest request). It is no longer used outside of the AuthenticationProvider interface itself.
  • Remove @InterfaceStability.Unstable annotation from the authenticateHttpRequestAsync method. When I introduced that annotation, I was under the impression that we didn't need it. However, in order to meet the requirements introduced in [broker][authentication]Support pass http auth status #14044, we need to let custom AuthenticationProviders add their own attributes.
  • Update the default implementation of authenticateHttpRequest. Because the previous implementation was unreachable by all auth providers except for the SASL implementation, this is a backwards compatible change.
  • Implement changes for the AuthenticationProviderToken so that it no longer relies on newHttpAuthState.

Verifying this change

I added new tests.

Does this pull request potentially affect one of the following parts:

  • The public API

This changes the public API within the broker by marking some methods as @Deprecated.

Documentation

  • doc-not-needed

We document the AuthenticationProvider interface in the code. I added these docs. There is currently no where else to update docs.

Matching PR in forked repository

PR in forked repository: michaeljmarshall#12

@@ -160,6 +163,20 @@ public String authenticate(AuthenticationDataSource authData) throws Authenticat
}
}

@Override
public boolean authenticateHttpRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
Copy link
Member

Choose a reason for hiding this comment

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

Don't need to override authenticateHttpRequest method.

See newHttpAuthState method, we have wrapped the request.

Copy link
Member Author

Choose a reason for hiding this comment

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

This PR deprecates newHttpAuthState because that method creates an object that is unnecessary and that triggers an authentication check for no reason in the OneStageAuthenticationState class.

Copy link
Member Author

Choose a reason for hiding this comment

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

Note that I am leaving newHttpAuthState as is in case someone already relies on the method itself.

Copy link
Member

Choose a reason for hiding this comment

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

Your PR is awesome! but I have second thoughts about whether to deprecate the newHttpAuthState, I don't suggest you deprecate this method,

  1. Using newHttpAuthState returns AuthenticationState, which includes role and authentication data, we can simply get these from AuthenticationState, and also quickly check the user authentication data.
  2. Keep the same logic with the newAuthState, it looks cleaner.

This PR deprecates newHttpAuthState because that method creates an object that is unnecessary and that triggers an authentication check for no reason in the OneStageAuthenticationState class.

I can accept authentication checks in the constructor of OneStageAuthenticationState, it is a quick check.

Maybe we can improve here, but the Pulsar must explicitly call the authenticate of AuthenticationState.

Copy link
Member Author

Choose a reason for hiding this comment

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

My primary motivation for this PR is to implement PIP 97. If we want authentication to be asynchronous, we cannot assume that when the AuthenticationState object is initialized, the authenticationDataSource and the authRole are present because the authentication might not yet be completed. My goal with this PR is to break an unnecessary relationship between AuthenticationState and http request authentication that was created by #14044.

  1. Using newHttpAuthState returns AuthenticationState, which includes role and authentication data, we can simply get these from AuthenticationState, and also quickly check the user authentication data.

This is not the intention of the newHttpAuthState method. The method was added by #14044 so that the AuthenticationDataSource for an AuthenticationProvider can be added on the http request as the AuthenticatedDataAttributeName attribute on the request. This PR proposes an alternate way to meet those criteria so that we can implement PIP 97.

  1. Keep the same logic with the newAuthState, it looks cleaner.

I disagree that it looks cleaner. The primary reason that we need a state object in the pulsar protocol handlers is that we track isExpired and whether or not the authentication is complete (first with isComplete and in 2.12 with the result of the authenticationAsync). We do not track state for individual HTTP request connections, so we shouldn't have a method to create a state object for HTTP requests.

Further, we're only building the AuthenticationState object to get the AuthenticationDataSource and we're not calling the AuthenticationState#authenticate method. There is no need for this indirection. We can give AuthenticationProvider implementations freedom to add the right request attributes by using the authenticateHttpRequest (and eventually authenticateHttpRequestAsync).

Also, another problem is with the AuthenticationDataSource in the AuthenticationState. I think we should recreate the AuthenticationDataSource each time we call authenticate. However, if we have newHttpAuthState, the AuthenticationState object is not able to create a new AuthenticationDataSource. I think the newHttpAuthState creates an confusion about how to use the AuthenticationState. This is a separate issue that I will follow up on in another PR.

I can accept authentication checks in the constructor of OneStageAuthenticationState, it is a quick check.

It does not matter that it is "quick". It is a waste of resources to verify authentication information twice.

Maybe we can improve here, but the Pulsar must explicitly call the authenticate of AuthenticationState.

This is already the way that Pulsar operates, but it has not been a stated requirement. I think this is essential for making authentication asynchronous. I will follow up with a separate PR.

Copy link
Member

Choose a reason for hiding this comment

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

Thank you for your explanation. This looks good to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you for sharing your concerns. It is great to have this context written down in the PR for future reference.

Copy link
Member

@lhotari lhotari left a comment

Choose a reason for hiding this comment

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

LGTM

@codecov-commenter
Copy link

Codecov Report

Merging #19197 (8786d14) into master (334c3a5) will increase coverage by 0.18%.
The diff coverage is 21.99%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master   #19197      +/-   ##
============================================
+ Coverage     47.04%   47.22%   +0.18%     
- Complexity     9190    18840    +9650     
============================================
  Files           607     1625    +1018     
  Lines         57677   132324   +74647     
  Branches       6007    14567    +8560     
============================================
+ Hits          27132    62491   +35359     
- Misses        27598    63528   +35930     
- Partials       2947     6305    +3358     
Flag Coverage Δ
unittests 47.22% <21.99%> (+0.18%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
.../broker/authentication/AuthenticationProvider.java 7.14% <0.00%> (ø)
...r/broker/authentication/AuthenticationService.java 0.00% <0.00%> (ø)
...apache/pulsar/broker/web/AuthenticationFilter.java 15.00% <0.00%> (ø)
...rg/apache/pulsar/broker/delayed/bucket/Bucket.java 0.00% <0.00%> (ø)
...r/delayed/bucket/BucketDelayedDeliveryTracker.java 0.00% <0.00%> (ø)
...layed/bucket/CombinedSegmentDelayedIndexQueue.java 0.00% <0.00%> (ø)
...ulsar/broker/delayed/bucket/DelayedIndexQueue.java 0.00% <0.00%> (ø)
.../pulsar/broker/delayed/bucket/ImmutableBucket.java 0.00% <0.00%> (ø)
...he/pulsar/broker/delayed/bucket/MutableBucket.java 0.00% <0.00%> (ø)
...ed/bucket/TripleLongPriorityDelayedIndexQueue.java 0.00% <0.00%> (ø)
... and 1065 more

@michaeljmarshall michaeljmarshall merged commit 3c38ed5 into apache:master Jan 19, 2023
@michaeljmarshall michaeljmarshall deleted the pip-97-http-requests-pre-work branch January 19, 2023 05:38
michaeljmarshall added a commit to michaeljmarshall/pulsar that referenced this pull request Apr 19, 2023
…pache#19197)

PIP: apache#12105

This is the first of several PRs to implement [PIP 97](apache#12105).

This PR seeks to start to solve the fact that the `AuthenticationState` class currently authenticates `authData` twice instead of just once. This change is important to make before we are able to utilize the async methods introduced in apache#12104.

Historical context: apache#14044 introduced the `AuthenticationProvider#newHttpAuthState`  method. The only use case for this method in the pulsar code base is to let custom providers specify the `AuthenticationDataSource` on http request attributes. The primary problem with that implementation is that the `AuthenticationState` class currently involves authenticating the `authData` passed in to the `newHttpAuthState`. As such, this code is sub-optimal, and creates a confusing flow.

I propose we deprecate the `newHttpAuthState` method and instead start using the `authenticateHttpRequestAsync` and `authenticateHttpRequest` methods to allow custom implementations to configure the desired `AuthenticationDataSource` on the request attributes.

In order to simplify the diff for reviewers, this PR uses the deprecated `AuthenticationProvider#authenticateHttpRequest` method. I plan to follow up and switch to use the `AuthenticationProvider#authenticateHttpRequestAsync` method.

Note that these changes are completely backwards compatible. The only risk is to users that have custom code loaded into the broker that calls the `AuthenticationProvider#authenticateHttpRequest` method.

* Deprecate `AuthenticationService#authenticateHttpRequest(HttpServletRequest request, AuthenticationDataSource authData)`. It is no longer used.
* Deprecate `AuthenticationProvider#newHttpAuthState(HttpServletRequest request)`. It is no longer used outside of the `AuthenticationProvider` interface itself.
* Remove `@InterfaceStability.Unstable` annotation from the `authenticateHttpRequestAsync` method. When I introduced that annotation, I was under the impression that we didn't need it. However, in order to meet the requirements introduced in apache#14044, we need to let custom `AuthenticationProviders` add their own attributes.
* Update the default implementation of `authenticateHttpRequest`. Because the previous implementation was unreachable by all auth providers except for the SASL implementation, this is a backwards compatible change.
* Implement changes for the `AuthenticationProviderToken` so that it no longer relies on `newHttpAuthState`.

I added new tests.

- [x] The public API

This changes the public API within the broker by marking some methods as `@Deprecated`.

- [x] `doc-not-needed`

We document the `AuthenticationProvider` interface in the code. I added these docs. There is currently no where else to update docs.

PR in forked repository: #12

My primary motivation for this PR is to implement PIP 97. If we want authentication to be asynchronous, we cannot assume that when the `AuthenticationState` object is initialized, the `authenticationDataSource` and the `authRole` are present because the authentication might not yet be completed. My goal with this PR is to break an unnecessary relationship between `AuthenticationState` and http request authentication that was created by apache#14044.

(cherry picked from commit 3c38ed5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/authn doc-not-needed Your PR changes do not impact docs ready-to-test
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants