From 10ad71a635067357791ca87a6abb305c48e25df2 Mon Sep 17 00:00:00 2001 From: Zixuan Liu Date: Mon, 18 Jul 2022 22:26:59 +0800 Subject: [PATCH] [improve][authentication] Support for get token from HTTP params Signed-off-by: Zixuan Liu --- .../AuthenticationProviderToken.java | 24 ++++++- .../AuthenticationProviderTokenTest.java | 68 ++++++++++++++++--- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java index 5c6d17a1d776b..9cc220aa8c75b 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java @@ -168,7 +168,7 @@ public AuthenticationState newAuthState(AuthData authData, SocketAddress remoteA @Override public AuthenticationState newHttpAuthState(HttpServletRequest request) throws AuthenticationException { - return new TokenAuthenticationState(this, request); + return new TokenAuthenticationState(this, new HttpServletRequestWrapper(request)); } public static String getToken(AuthenticationDataSource authData) throws AuthenticationException { @@ -387,4 +387,26 @@ public boolean isExpired() { return expiration < System.currentTimeMillis(); } } + + private static final class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper { + private final HttpServletRequest request; + + public HttpServletRequestWrapper(HttpServletRequest request) { + super(request); + this.request = request; + } + + @Override + public String getHeader(String name) { + // The browser javascript WebSocket client couldn't add the auth param to the request header, use the + // query param `token` to transport the auth token for the browser javascript WebSocket client. + if (name.equals(HTTP_HEADER_NAME) && request.getHeader(HTTP_HEADER_NAME) == null) { + String token = request.getParameter(TOKEN); + if (token != null) { + return !token.startsWith(HTTP_HEADER_VALUE_PREFIX) ? HTTP_HEADER_VALUE_PREFIX + token : token; + } + } + return super.getHeader(name); + } + } } diff --git a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java index 0d29354915684..c7ab278130021 100644 --- a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java +++ b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java @@ -19,12 +19,13 @@ package org.apache.pulsar.broker.authentication; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; - import com.google.common.collect.Lists; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwt; @@ -33,26 +34,24 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; -import java.security.Key; -import java.util.Arrays; -import java.util.List; -import lombok.Cleanup; - import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.security.Key; import java.security.KeyPair; import java.security.PrivateKey; import java.sql.Date; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Optional; import java.util.Properties; import java.util.concurrent.TimeUnit; - import javax.crypto.SecretKey; import javax.naming.AuthenticationException; - +import javax.servlet.http.HttpServletRequest; +import lombok.Cleanup; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.utils.AuthTokenUtils; import org.apache.pulsar.common.api.AuthData; @@ -837,7 +836,7 @@ public void testTokenSettingPrefix() throws Exception { ServiceConfiguration conf = new ServiceConfiguration(); conf.setProperties(properties); - ServiceConfiguration mockConf = Mockito.mock(ServiceConfiguration.class); + ServiceConfiguration mockConf = mock(ServiceConfiguration.class); String prefix = "test"; Mockito.when(mockConf.getProperty(anyString())) @@ -864,6 +863,57 @@ public void testTokenSettingPrefix() throws Exception { .getProperty(prefix + AuthenticationProviderToken.CONF_TOKEN_AUDIENCE); } + @Test + public void testTokenFromHttpParams() throws Exception { + SecretKey secretKey = AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256); + + @Cleanup + AuthenticationProviderToken provider = new AuthenticationProviderToken(); + + Properties properties = new Properties(); + properties.setProperty(AuthenticationProviderToken.CONF_TOKEN_SECRET_KEY, + AuthTokenUtils.encodeKeyBase64(secretKey)); + + ServiceConfiguration conf = new ServiceConfiguration(); + conf.setProperties(properties); + provider.initialize(conf); + + String token = AuthTokenUtils.createToken(secretKey, SUBJECT, Optional.empty()); + HttpServletRequest servletRequest = mock(HttpServletRequest.class); + doReturn(token).when(servletRequest).getParameter("token"); + doReturn(null).when(servletRequest).getHeader("Authorization"); + doReturn("127.0.0.1").when(servletRequest).getRemoteAddr(); + doReturn(0).when(servletRequest).getRemotePort(); + + AuthenticationState authState = provider.newHttpAuthState(servletRequest); + provider.authenticate(authState.getAuthDataSource()); + } + + @Test + public void testTokenFromHttpHeaders() throws Exception { + SecretKey secretKey = AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256); + + @Cleanup + AuthenticationProviderToken provider = new AuthenticationProviderToken(); + + Properties properties = new Properties(); + properties.setProperty(AuthenticationProviderToken.CONF_TOKEN_SECRET_KEY, + AuthTokenUtils.encodeKeyBase64(secretKey)); + + ServiceConfiguration conf = new ServiceConfiguration(); + conf.setProperties(properties); + provider.initialize(conf); + + String token = AuthTokenUtils.createToken(secretKey, SUBJECT, Optional.empty()); + HttpServletRequest servletRequest = mock(HttpServletRequest.class); + doReturn("Bearer " + token).when(servletRequest).getHeader("Authorization"); + doReturn("127.0.0.1").when(servletRequest).getRemoteAddr(); + doReturn(0).when(servletRequest).getRemotePort(); + + AuthenticationState authState = provider.newHttpAuthState(servletRequest); + provider.authenticate(authState.getAuthDataSource()); + } + private static String createTokenWithAudience(Key signingKey, String audienceClaim, List audience) { JwtBuilder builder = Jwts.builder() .setSubject(SUBJECT)