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

[broker][authentication]Support pass http auth status #14044

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
77b049f
Support pass http auth status
tuteng Jan 29, 2022
ce3c5d3
Fixed comment and style
tuteng Jan 29, 2022
1badfaa
Add method name for oauth2 auth
tuteng Jan 29, 2022
cbc79e2
Change var type to private from public
tuteng Jan 29, 2022
8924aa6
Fixed style check
tuteng Jan 29, 2022
3cc0a9d
Fixed function interface and client api var
tuteng Feb 3, 2022
3407283
Update auth method name
tuteng Feb 3, 2022
a4d8718
Backward compatible
tuteng Feb 3, 2022
78c7726
Merge branch 'master' into fixed/support-pass-http-auth-status
tuteng Feb 3, 2022
713b1d1
Fixed style
tuteng Feb 3, 2022
6c34f62
Delete whitespace
tuteng Feb 3, 2022
8c2ef67
Reduce length
tuteng Feb 3, 2022
c9f8000
Update test
tuteng Feb 3, 2022
3fb6e3b
Update remote addr
tuteng Feb 3, 2022
97ae44d
Fixed authentication token test
tuteng Feb 4, 2022
197411f
Fixed authentication token test
tuteng Feb 4, 2022
b20a7d5
Update var type, add final field, add comment for authentication method
tuteng Feb 9, 2022
0467c96
Update comment
tuteng Feb 10, 2022
3fc7d57
Fixed test
tuteng Feb 10, 2022
5b08617
Fixed auth test
tuteng Feb 11, 2022
ae5b447
Update comment
tuteng Feb 16, 2022
e4efa86
Revert code
tuteng Feb 16, 2022
bb1ebfe
Remove no used var
tuteng Feb 23, 2022
3764121
Update map headers to unmodifiableMap type
tuteng Feb 23, 2022
673818c
Merge branch 'master' into fixed/support-pass-http-auth-status
tuteng Feb 27, 2022
49c1080
Remove redundant final
tuteng Feb 27, 2022
b1def58
Fixed style
tuteng Feb 27, 2022
485e961
Fixed comment
tuteng Feb 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ default AuthenticationState newAuthState(AuthData authData,
return new OneStageAuthenticationState(authData, remoteAddress, sslSession, this);
}

/**
* Create an http authentication data State use passed in AuthenticationDataSource.
*/
default AuthenticationState newHttpAuthState(HttpServletRequest request)
throws AuthenticationException {
return new OneStageAuthenticationState(request, this);
}

/**
* Validate the authentication for the given credentials with the specified authentication data.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,37 @@ public AuthenticationState newAuthState(AuthData authData, SocketAddress remoteA
}
}

@Override
public AuthenticationState newHttpAuthState(HttpServletRequest request) throws AuthenticationException {
final List<AuthenticationState> states = new ArrayList<>(providers.size());

AuthenticationException authenticationException = null;
try {
applyAuthProcessor(
providers,
provider -> {
AuthenticationState state = provider.newHttpAuthState(request);
states.add(state);
return state;
}
);
} catch (AuthenticationException ae) {
authenticationException = ae;
}
if (states.isEmpty()) {
log.debug("Failed to initialize a new http auth state from {}",
request.getRemoteHost(), authenticationException);
if (authenticationException != null) {
throw authenticationException;
} else {
throw new AuthenticationException(
"Failed to initialize a new http auth state from " + request.getRemoteHost());
}
} else {
return new AuthenticationListState(states);
}
}

@Override
public boolean authenticateHttpRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
Boolean authenticated = applyAuthProcessor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ public AuthenticationService(ServiceConfiguration conf) throws PulsarServerExcep

public String authenticateHttpRequest(HttpServletRequest request) throws AuthenticationException {
AuthenticationException authenticationException = null;
AuthenticationDataSource authData = new AuthenticationDataHttps(request);
String authMethodName = request.getHeader("X-Pulsar-Auth-Method-Name");

if (authMethodName != null) {
Expand All @@ -96,6 +95,8 @@ public String authenticateHttpRequest(HttpServletRequest request) throws Authent
throw new AuthenticationException(
String.format("Unsupported authentication method: [%s].", authMethodName));
}
AuthenticationState authenticationState = providerToUse.newHttpAuthState(request);
AuthenticationDataSource authData = authenticationState.getAuthDataSource();
try {
return providerToUse.authenticate(authData);
} catch (AuthenticationException e) {
Expand All @@ -109,6 +110,8 @@ public String authenticateHttpRequest(HttpServletRequest request) throws Authent
} else {
for (AuthenticationProvider provider : providers.values()) {
try {
AuthenticationState authenticationState = provider.newHttpAuthState(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 is necessary because providers are a list

AuthenticationDataSource authData = authenticationState.getAuthDataSource();
return provider.authenticate(authData);
} catch (AuthenticationException e) {
if (LOG.isDebugEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.net.SocketAddress;
import javax.naming.AuthenticationException;
import javax.net.ssl.SSLSession;
import javax.servlet.http.HttpServletRequest;
import org.apache.pulsar.common.api.AuthData;

import static java.nio.charset.StandardCharsets.UTF_8;
Expand All @@ -46,6 +47,12 @@ public OneStageAuthenticationState(AuthData authData,
this.authRole = provider.authenticate(authenticationDataSource);
}

public OneStageAuthenticationState(HttpServletRequest request, AuthenticationProvider provider)
throws AuthenticationException {
this.authenticationDataSource = new AuthenticationDataHttps(request);
this.authRole = provider.authenticate(authenticationDataSource);
}

@Override
public String getAuthRole() {
return authRole;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import org.apache.pulsar.broker.authentication.AuthenticationDataHttps;
import org.apache.pulsar.broker.authentication.AuthenticationService;
import org.apache.pulsar.broker.authentication.AuthenticationState;
import org.apache.pulsar.common.sasl.SaslConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -76,8 +77,15 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
// not sasl type, return role directly.
String role = authenticationService.authenticateHttpRequest((HttpServletRequest) request);
request.setAttribute(AuthenticatedRoleAttributeName, role);
request.setAttribute(AuthenticatedDataAttributeName,
new AuthenticationDataHttps((HttpServletRequest) request));
String authMethodName = httpRequest.getHeader("X-Pulsar-Auth-Method-Name");
if (authMethodName != null && authenticationService.getAuthenticationProvider(authMethodName) != null) {
AuthenticationState authenticationState = authenticationService
.getAuthenticationProvider(authMethodName).newHttpAuthState(httpRequest);
request.setAttribute(AuthenticatedDataAttributeName, authenticationState.getAuthDataSource());
} else {
request.setAttribute(AuthenticatedDataAttributeName,
Copy link
Member Author

Choose a reason for hiding this comment

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

Backward compatible

new AuthenticationDataHttps((HttpServletRequest) request));
}
if (LOG.isDebugEnabled()) {
LOG.debug("[{}] Authenticated HTTP request with role {}", request.getRemoteAddr(), role);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
package org.apache.pulsar.broker.authentication;

import static java.nio.charset.StandardCharsets.UTF_8;
import javax.servlet.http.HttpServletRequest;
import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
Expand Down Expand Up @@ -165,6 +167,14 @@ private AuthenticationState newAuthState(String token, String expectedSubject) t
return authState;
}

private AuthenticationState newHttpAuthState(HttpServletRequest request, String expectedSubject) throws Exception {
AuthenticationState authState = authProvider.newHttpAuthState(request);
assertEquals(authState.getAuthRole(), expectedSubject);
assertTrue(authState.isComplete());
assertFalse(authState.isExpired());
return authState;
}

private void verifyAuthStateExpired(AuthenticationState authState, String expectedSubject)
throws Exception {
assertEquals(authState.getAuthRole(), expectedSubject);
Expand All @@ -188,4 +198,20 @@ public void testNewAuthState() throws Exception {

}

@Test
public void testNewHttpAuthState() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
AuthenticationState authStateAA = newHttpAuthState(request, SUBJECT_A);
AuthenticationState authStateAB = newHttpAuthState(request, SUBJECT_B);
AuthenticationState authStateBA = newHttpAuthState(request, SUBJECT_A);
AuthenticationState authStateBB = newHttpAuthState(request, SUBJECT_B);

Thread.sleep(TimeUnit.SECONDS.toMillis(6));

verifyAuthStateExpired(authStateAA, SUBJECT_A);
verifyAuthStateExpired(authStateAB, SUBJECT_B);
verifyAuthStateExpired(authStateBA, SUBJECT_A);
verifyAuthStateExpired(authStateBB, SUBJECT_B);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@

public class AuthenticationDataBasic implements AuthenticationDataProvider {
private static final String HTTP_HEADER_NAME = "Authorization";
private static final String PULSAR_AUTH_METHOD_NAME = "X-Pulsar-Auth-Method-Name";
private String httpAuthToken;
private String commandAuthToken;

public AuthenticationDataBasic(String userId, String password) {
httpAuthToken = "Basic " + Base64.getEncoder().encodeToString((userId + ":" + password).getBytes());
commandAuthToken = userId+":"+password;
commandAuthToken = userId + ":" + password;
}

@Override
Expand All @@ -46,6 +47,7 @@ public boolean hasDataForHttp() {
public Set<Map.Entry<String, String>> getHttpHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put(HTTP_HEADER_NAME, httpAuthToken);
headers.put(PULSAR_AUTH_METHOD_NAME, "basic");
return headers.entrySet();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import org.apache.pulsar.client.api.AuthenticationDataProvider;
Expand All @@ -35,6 +38,8 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

public class AuthenticationDataTls implements AuthenticationDataProvider {
private static final String PULSAR_AUTH_METHOD_NAME = "X-Pulsar-Auth-Method-Name";

private static final long serialVersionUID = 1L;
protected X509Certificate[] tlsCertificates;
protected PrivateKey tlsPrivateKey;
Expand Down Expand Up @@ -88,6 +93,11 @@ public boolean hasDataForTls() {
return true;
}

@Override
public Set<Map.Entry<String, String>> getHttpHeaders() {
return Collections.singletonMap(PULSAR_AUTH_METHOD_NAME, "tls").entrySet();
}

@Override
public Certificate[] getTlsCertificates() {
if (certFile != null && certFile.checkAndRefresh()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package org.apache.pulsar.client.impl.auth;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
Expand All @@ -29,6 +29,8 @@
public class AuthenticationDataToken implements AuthenticationDataProvider {
public static final String HTTP_HEADER_NAME = "Authorization";

private static final String PULSAR_AUTH_METHOD_NAME = "X-Pulsar-Auth-Method-Name";

private final Supplier<String> tokenSupplier;

public AuthenticationDataToken(Supplier<String> tokenSupplier) {
Expand All @@ -42,7 +44,10 @@ public boolean hasDataForHttp() {

@Override
public Set<Map.Entry<String, String>> getHttpHeaders() {
return Collections.singletonMap(HTTP_HEADER_NAME, "Bearer " + getToken()).entrySet();
Map<String, String> headers = new HashMap<>();
headers.put(PULSAR_AUTH_METHOD_NAME, "token");
headers.put(HTTP_HEADER_NAME, "Bearer " + getToken());
return headers.entrySet();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
package org.apache.pulsar.client.impl.auth.oauth2;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.pulsar.client.api.AuthenticationDataProvider;
Expand All @@ -28,13 +28,12 @@
*/
class AuthenticationDataOAuth2 implements AuthenticationDataProvider {
public static final String HTTP_HEADER_NAME = "Authorization";
private static final String PULSAR_AUTH_METHOD_NAME = "X-Pulsar-Auth-Method-Name";

private final String accessToken;
private final Set<Map.Entry<String, String>> headers;

public AuthenticationDataOAuth2(String accessToken) {
this.accessToken = accessToken;
this.headers = Collections.singletonMap(HTTP_HEADER_NAME, "Bearer " + accessToken).entrySet();
}

@Override
Expand All @@ -44,7 +43,10 @@ public boolean hasDataForHttp() {

@Override
public Set<Map.Entry<String, String>> getHttpHeaders() {
return this.headers;
Map<String, String> headers = new HashMap<>();
headers.put(HTTP_HEADER_NAME, "Bearer " + accessToken);
headers.put(PULSAR_AUTH_METHOD_NAME, "token");
return headers.entrySet();
}

@Override
Expand Down