diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java index 4e1d33b5ec5e7..0c262f5c62056 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java @@ -19,7 +19,6 @@ package org.apache.pulsar.broker.authentication; import java.security.cert.X509Certificate; - import javax.servlet.http.HttpServletRequest; public class AuthenticationDataHttps extends AuthenticationDataHttp { @@ -27,7 +26,7 @@ public class AuthenticationDataHttps extends AuthenticationDataHttp { protected final X509Certificate[] certificates; public AuthenticationDataHttps(HttpServletRequest request) { - super(request); + super(new AuthenticationProviderToken.HttpServletRequestWrapper(request)); certificates = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); } 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 80b9978fc43bf..d5e99403e9fff 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 @@ -19,33 +19,30 @@ package org.apache.pulsar.broker.authentication; import static java.nio.charset.StandardCharsets.UTF_8; - +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.JwtParser; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.SignatureException; +import io.prometheus.client.Counter; +import io.prometheus.client.Histogram; import java.io.IOException; import java.net.SocketAddress; import java.security.Key; - import java.util.Date; import java.util.List; import javax.naming.AuthenticationException; import javax.net.ssl.SSLSession; - -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.JwtParser; -import io.prometheus.client.Counter; -import io.prometheus.client.Histogram; +import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.metrics.AuthenticationMetrics; import org.apache.pulsar.broker.authentication.utils.AuthTokenUtils; import org.apache.pulsar.common.api.AuthData; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwt; -import io.jsonwebtoken.JwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.security.SignatureException; - public class AuthenticationProviderToken implements AuthenticationProvider { final static String HTTP_HEADER_NAME = "Authorization"; @@ -351,4 +348,25 @@ public boolean isExpired() { return expiration < System.currentTimeMillis(); } } + public 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 5a2e1c4ea48ac..1e328050dacc8 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 @@ -18,13 +18,13 @@ */ package org.apache.pulsar.broker.authentication; +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.assertNull; 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,25 +33,23 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; -import java.security.Key; -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.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; @@ -800,4 +798,55 @@ public String getCommandData() { assertEquals(subject, SUBJECT); provider.close(); } + + @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(); + + AuthenticationDataHttps authenticationDataHttps = new AuthenticationDataHttps(servletRequest); + provider.authenticate(authenticationDataHttps); + } + + @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(); + + AuthenticationDataHttps authenticationDataHttps = new AuthenticationDataHttps(servletRequest); + provider.authenticate(authenticationDataHttps); + } }