Skip to content

Commit

Permalink
Add fix to be able to use NKey + Seed auth handler and username + pas…
Browse files Browse the repository at this point in the history
…sword information combined. (#1138)
  • Loading branch information
ryad-eldajani authored May 20, 2024
1 parent 1de9fa3 commit 87b0abc
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 41 deletions.
79 changes: 39 additions & 40 deletions src/main/java/io/nats/client/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -2399,51 +2399,50 @@ public CharBuffer buildProtocolConnectOptionsString(String serverURI, boolean in
appendOption(connectString, Options.OPTION_SIG, encodedSig, true, true);
appendOption(connectString, Options.OPTION_JWT, jwt, true, true);
}
else {
String uriUser = null;
String uriPass = null;
String uriToken = null;

// Values from URI override options
try {
URI uri = this.createURIForServer(serverURI);
String userInfo = uri.getRawUserInfo();
if (userInfo != null) {
int at = userInfo.indexOf(":");
if (at == -1) {
uriToken = uriDecode(userInfo);
}
else {
uriUser = uriDecode(userInfo.substring(0, at));
uriPass = uriDecode(userInfo.substring(at + 1));
}

String uriUser = null;
String uriPass = null;
String uriToken = null;

// Values from URI override options
try {
URI uri = this.createURIForServer(serverURI);
String userInfo = uri.getRawUserInfo();
if (userInfo != null) {
int at = userInfo.indexOf(":");
if (at == -1) {
uriToken = uriDecode(userInfo);
}
else {
uriUser = uriDecode(userInfo.substring(0, at));
uriPass = uriDecode(userInfo.substring(at + 1));
}
}
catch (URISyntaxException e) {
// the createURIForServer call is the one that potentially throws this
// uriUser, uriPass and uriToken will already be null
}
}
catch (URISyntaxException e) {
// the createURIForServer call is the one that potentially throws this
// uriUser, uriPass and uriToken will already be null
}

if (uriUser != null) {
appendOption(connectString, Options.OPTION_USER, uriUser, true, true);
}
else if (this.username != null) {
appendOption(connectString, Options.OPTION_USER, this.username, true, true);
}
if (uriUser != null) {
appendOption(connectString, Options.OPTION_USER, uriUser, true, true);
}
else if (this.username != null) {
appendOption(connectString, Options.OPTION_USER, this.username, true, true);
}

if (uriPass != null) {
appendOption(connectString, Options.OPTION_PASSWORD, uriPass, true, true);
}
else if (this.password != null) {
appendOption(connectString, Options.OPTION_PASSWORD, this.password, true, true);
}
if (uriPass != null) {
appendOption(connectString, Options.OPTION_PASSWORD, uriPass, true, true);
}
else if (this.password != null) {
appendOption(connectString, Options.OPTION_PASSWORD, this.password, true, true);
}

if (uriToken != null) {
appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
}
else if (this.token != null) {
appendOption(connectString, Options.OPTION_AUTH_TOKEN, this.token, true, true);
}
if (uriToken != null) {
appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
}
else if (this.token != null) {
appendOption(connectString, Options.OPTION_AUTH_TOKEN, this.token, true, true);
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/test/java/io/nats/client/AuthHandlerForTesting.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@

public class AuthHandlerForTesting implements AuthHandler {
private final NKey nkey;
private final char[] jwt;

public AuthHandlerForTesting(NKey nkey) {
this.nkey = nkey;
this.jwt = null;
}

public AuthHandlerForTesting(NKey nkey, char[] jwt) {
this.nkey = nkey;
this.jwt = jwt;
}

public AuthHandlerForTesting() throws Exception {
this.nkey = NKey.createUser(null);
this.jwt = null;
}

public NKey getNKey() {
Expand All @@ -48,6 +56,6 @@ public byte[] sign(byte[] nonce) {
}

public char[] getJWT() {
return null;
return this.jwt;
}
}
45 changes: 45 additions & 0 deletions src/test/java/io/nats/client/OptionsTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,51 @@ public void testNKeyConnectOptions() throws Exception {
assertEquals(expectedWithAuth, o.buildProtocolConnectOptionsString("nats://localhost:4222", true, nonce).toString(), "auth connect options");
}

// Test for auth handler from nkey, option JWT and user info
@Test
public void testNKeyJWTAndUserInfoOptions() throws Exception {
// "jwt" is encoded from:
// Header: {"alg":"HS256"}
// Payload: {"jti":"","iat":2000000000,"iss":"","name":"user_jwt","sub":"","nats":{"pub":{"deny":[">"]},
// "sub":{"deny":[">"]},"subs":-1,"data":-1,"payload":-1,"type":"user","version":2}}
String jwt = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIiLCJpYXQiOjIwMDAwMDAwMDAsImlzcyI6IiIsIm5hbWUiOiJ1c2VyX2p3"
+ "dCIsInN1YiI6IiIsIm5hdHMiOnsicHViIjp7ImRlbnkiOlsiPiJdfSwic3ViIjp7ImRlbnkiOlsiPiJdfSwic3VicyI6LTEsImRh"
+ "dGEiOi0xLCJwYXlsb2FkIjotMSwidHlwZSI6InVzZXIiLCJ2ZXJzaW9uIjoyfX0";
NKey nkey = NKey.createUser(null);
String username = "username";
String password = "password";
AuthHandlerForTesting th = new AuthHandlerForTesting(nkey, jwt.toCharArray());
byte[] nonce = "abcdefg".getBytes(StandardCharsets.UTF_8);
String sig = Base64.getUrlEncoder().withoutPadding().encodeToString(th.sign(nonce));

// Assert that no auth and user info is given
Options options = new Options.Builder().authHandler(th)
.userInfo(username.toCharArray(), password.toCharArray()).build();
String expectedWithoutAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\""
+ ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true,"
+ "\"headers\":true,\"no_responders\":true}";
String actualWithoutAuth = options
.buildProtocolConnectOptionsString("nats://localhost:4222", false, nonce).toString();
assertEquals(expectedWithoutAuth, actualWithoutAuth);

// Assert that auth and user info is given via options
String expectedWithAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\""
+ ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true,"
+ "\"headers\":true,\"no_responders\":true,\"nkey\":\"" + new String(th.getID()) + "\",\"sig\":\""
+ sig + "\",\"jwt\":\"" + jwt + "\",\"user\":\"" + username + "\",\"pass\":\"" + password + "\"}";
String actualWithAuthInOptions = options
.buildProtocolConnectOptionsString("nats://localhost:4222", true, nonce).toString();
assertEquals(expectedWithAuth, actualWithAuthInOptions);

// Assert that auth is given via options and user info is given via server URI
Options optionsWithoutUserInfo = new Options.Builder().authHandler(th).build();
String serverUriWithAuth = "nats://" + username + ":" + password + "@localhost:4222";
String actualWithAuthInServerUri = optionsWithoutUserInfo
.buildProtocolConnectOptionsString(serverUriWithAuth, true, nonce).toString();
assertEquals(expectedWithAuth, actualWithAuthInServerUri);
}


@Test
public void testDefaultDataPort() {
Options o = new Options.Builder().socketWriteTimeout(null).build();
Expand Down

0 comments on commit 87b0abc

Please sign in to comment.