-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Add basic authentication plugin #1087
Conversation
b1c0c79
to
1045a8e
Compare
@@ -0,0 +1,6 @@ | |||
{ | |||
"super": { | |||
"password": "superPassword", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seriously? You should google "htpasswd".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, let's use common format for basic user:password file. That would eliminate the needs for a specific script/tool to manage it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed it and made userId equivalent to role.
448d1b1
to
1a76ca0
Compare
|
||
public static final String AuthenticatedRoleAttributeName = AuthenticationFilter.class.getName() + "-role"; | ||
|
||
public AuthenticationFilter(PulsarService pulsar) { | ||
this.authenticationService = pulsar.getBrokerService().getAuthenticationService(); | ||
if (pulsar.getConfiguration().getAuthenticationProviders() | ||
.contains(AuthenticationProviderBasic.class.getName())) { | ||
isBasic = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than a ad-hoc solution, is it possible to have a general way to trigger the right header in the response?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can modify AuthenticationService::authenticateHttpRequest
. Currently we just ignore exceptions in the method to try all auth providers, but probably we should gather information from the providers. Because "WWW-Authenticate" header can contain multiple challenges, we can suggest all supported authentication types at the first 401 response.
https://tools.ietf.org/html/rfc7235#section-4.1
Also, there is (at least) another response header, "Authentication-Info" (https://tools.ietf.org/html/rfc7615), that will carry information when authentication succeeds. So it would be better to design the API for this mechanism so that providers can respond with arbitrary headers and/or body data regardless of authentication results.
https://tools.ietf.org/html/rfc7235#section-2.1
HTTP does not restrict applications to this simple challenge-response
framework for access authentication. Additional mechanisms can be
used, such as authentication at the transport level or via message
encapsulation, and with additional header fields specifying
authentication information. However, such additional mechanisms are
not defined by this specification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think modifying authenticateHttpRequest
is not efficient because the information is gathered every time authentication failed.
I fixed AuthentcationFilter class to gather information from providers when it is initialized.
There was a problem hiding this comment.
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 are always be able to gather information on initialization. For example, Digest authentication needs to generate a challenge (nonce) every time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense.
I added PulsarHttpAuthenticationException class, which has realm information for WWW-Authentication
header and the information is gathered in authenticateHttpRequest
.
private String authToken; | ||
|
||
public AuthenticationDataBasic(String userId, String password) { | ||
authToken = "Basic " + Base64.getEncoder().encodeToString((userId + ":" + password).getBytes()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember correctly, authentication method name is stored in some field of a command. "Basic " is needed for the HTTP header but it's redundant for the pulsar's command.
Also, base64 encoding is needed because of a limitation of HTTP headers. I don't think pulsar's command has the same limitation.
They both increases data length unnecessarily。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made command data not contain encoded params.
return password; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing new line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
f5b4fec
to
4def43d
Compare
@@ -81,12 +81,15 @@ public String authenticate(AuthenticationDataSource authData, String authMethodN | |||
} | |||
} | |||
|
|||
public String authenticateHttpRequest(HttpServletRequest request) throws AuthenticationException { | |||
public String authenticateHttpRequest(HttpServletRequest request) throws PulsarHttpAuthenticationException { | |||
PulsarHttpAuthenticationException exception = new PulsarHttpAuthenticationException("Authentication required"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating the exception is a very costly operation because it fills up the stack trace, we should only do it in the failure path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it create realmInformation
string to avoid creating an exception.
b201dae
to
601e363
Compare
@@ -0,0 +1,2 @@ | |||
superUser:mQQQIsyvvKRtU |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure but should we add this file under license exclude list ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added it.
HttpServletResponse httpResponse = (HttpServletResponse) response; | ||
if (isNotBlank(e.getRealmInformation())) { | ||
((HttpServletResponse) response).setHeader("WWW-Authenticate", e.getRealmInformation()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I wrote on the previous comment, there is another standard header that is designed for returning information on authentication success, and authentication methods are also allowed to use other non-standard headers. Requiring auth providers to embed the header information into an exception and assuming the header name is "WWW-Authenticate" would be a limitation in the future.
Honestly speaking, I think using an exception to indicate simple authentication failure is a bad design, at least in Java, because of its cost. Authentication failures happen occasionally, and it can be caused easily by attackers. We shouldn't use exceptions for such a expectable case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maskit
I roughly agree with you.
using an exception to indicate simple authentication failure is a bad design
I think this should not be fixed in this PR adding authentication plugin.
We should consider good design and make another PR.
That's why, I exclude the code about authentication header from this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright.
a60b83d
to
8dbb49c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are few points need to be improved later (e.g. HTTP support, role management), but seems good. Let's revisit them after we improve API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Motivation
Some competitors of Pulsar has the basic authentication plugin.
Modifications
Added the basic authentication plugin.
Result
Pulsar can use basic authentication.