From 6f88ef03531d93dcf6fd8d42fd930cbf6ee15d08 Mon Sep 17 00:00:00 2001 From: coderzc Date: Fri, 10 May 2024 15:46:31 +0800 Subject: [PATCH 1/4] Supports creating tokens with additional headers --- .../authentication/utils/AuthTokenUtils.java | 9 ++++- .../utils/auth/tokens/TokensCliUtils.java | 8 ++++- .../utils/auth/tokens/TokensCliUtilsTest.java | 36 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/utils/AuthTokenUtils.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/utils/AuthTokenUtils.java index cb917a9e0bf00..e29b106f4f17e 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/utils/AuthTokenUtils.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/utils/AuthTokenUtils.java @@ -35,6 +35,7 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Date; +import java.util.Map; import java.util.Optional; import javax.crypto.SecretKey; import lombok.experimental.UtilityClass; @@ -89,16 +90,22 @@ public static String encodeKeyBase64(Key key) { return Encoders.BASE64.encode(key.getEncoded()); } - public static String createToken(Key signingKey, String subject, Optional expiryTime) { + public static String createToken(Key signingKey, String subject, Optional expiryTime, + Optional> headers) { JwtBuilder builder = Jwts.builder() .setSubject(subject) .signWith(signingKey); expiryTime.ifPresent(builder::setExpiration); + headers.ifPresent(builder::setHeaderParams); return builder.compact(); } + public static String createToken(Key signingKey, String subject, Optional expiryTime) { + return createToken(signingKey, subject, expiryTime, Optional.empty()); + } + public static byte[] readKeyFromUrl(String keyConfUrl) throws IOException { if (keyConfUrl.startsWith("data:") || keyConfUrl.startsWith("file:")) { try { diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java b/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java index 4ae28b2c0bdb8..fa70bcfd94af0 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java @@ -34,6 +34,7 @@ import java.security.Key; import java.security.KeyPair; import java.util.Date; +import java.util.Map; import java.util.Optional; import java.util.concurrent.Callable; import javax.crypto.SecretKey; @@ -140,6 +141,11 @@ public static class CommandCreateToken implements Callable { description = "Pass the private key for signing the token. This can either be: data:, file:, etc..") private String privateKey; + @Option(names = {"-h", + "--headers"}, + description = "Additional headers to token. Format: --headers key1=value1") + private Map headers; + @Override public Integer call() throws Exception { if (secretKey == null && privateKey == null) { @@ -166,7 +172,7 @@ public Integer call() throws Exception { ? Optional.empty() : Optional.of(new Date(System.currentTimeMillis() + expiryTime)); - String token = AuthTokenUtils.createToken(signingKey, subject, optExpiryTime); + String token = AuthTokenUtils.createToken(signingKey, subject, optExpiryTime, Optional.ofNullable(headers)); System.out.println(token); return 0; diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java index d5dc259438ea8..2a9800d2e4dc1 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java @@ -18,7 +18,12 @@ */ package org.apache.pulsar.utils.auth.tokens; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.io.Decoders; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.lang.reflect.Field; @@ -31,6 +36,37 @@ */ public class TokensCliUtilsTest { + @Test + public void testCreateToken() { + PrintStream oldStream = System.out; + try { + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(baoStream)); + + new TokensCliUtils().execute(new String[]{"create-secret-key", "--base64"}); + String secretKey = baoStream.toString(); + + baoStream.reset(); + + new TokensCliUtils().execute(new String[]{"create", "--secret-key", "data:;base64," + secretKey, "--subject", "test", "--headers", "kid=test"}); + String token = baoStream.toString(); + + Jwt jwt = Jwts.parserBuilder() + .setSigningKey(Decoders.BASE64.decode(secretKey)) + .build() + .parse(token); + + JwsHeader header = (JwsHeader) jwt.getHeader(); + String keyId = header.getKeyId(); + assertEquals(keyId, "test"); + + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + System.setOut(oldStream); + } + } + /** * Test tokens generate docs. * From 6dd063b0313483c2d54735c01c9391e9b4dfd414 Mon Sep 17 00:00:00 2001 From: coderzc Date: Fri, 10 May 2024 19:04:10 +0800 Subject: [PATCH 2/4] "-h" -> "-hs",avoid conflicts with the help command --- .../org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java b/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java index fa70bcfd94af0..78268a6295c28 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java @@ -141,7 +141,7 @@ public static class CommandCreateToken implements Callable { description = "Pass the private key for signing the token. This can either be: data:, file:, etc..") private String privateKey; - @Option(names = {"-h", + @Option(names = {"-hs", "--headers"}, description = "Additional headers to token. Format: --headers key1=value1") private Map headers; From 1741dde1661465033226741fc11e61e1d499829d Mon Sep 17 00:00:00 2001 From: coderzc Date: Sat, 11 May 2024 14:32:36 +0800 Subject: [PATCH 3/4] fix test --- .../org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java index 2a9800d2e4dc1..a03c19d5a3172 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java @@ -54,7 +54,7 @@ public void testCreateToken() { Jwt jwt = Jwts.parserBuilder() .setSigningKey(Decoders.BASE64.decode(secretKey)) .build() - .parse(token); + .parseClaimsJws(token); JwsHeader header = (JwsHeader) jwt.getHeader(); String keyId = header.getKeyId(); From aef7cfc8907d00bcabf5f339a87cde300d2cba78 Mon Sep 17 00:00:00 2001 From: coderzc Date: Fri, 17 May 2024 17:23:03 +0800 Subject: [PATCH 4/4] add a case with a non-reserved jwt header --- .../pulsar/utils/auth/tokens/TokensCliUtilsTest.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java index a03c19d5a3172..65c5d9981bfd2 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java @@ -48,7 +48,14 @@ public void testCreateToken() { baoStream.reset(); - new TokensCliUtils().execute(new String[]{"create", "--secret-key", "data:;base64," + secretKey, "--subject", "test", "--headers", "kid=test"}); + String[] command = {"create", "--secret-key", + "data:;base64," + secretKey, + "--subject", "test", + "--headers", "kid=test", + "--headers", "my-k=my-v" + }; + + new TokensCliUtils().execute(command); String token = baoStream.toString(); Jwt jwt = Jwts.parserBuilder() @@ -59,6 +66,7 @@ public void testCreateToken() { JwsHeader header = (JwsHeader) jwt.getHeader(); String keyId = header.getKeyId(); assertEquals(keyId, "test"); + assertEquals(header.get("my-k"), "my-v"); } catch (Exception e) { throw new RuntimeException(e);