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

Accept encoded connection urls #674

Merged
merged 10 commits into from
Jun 30, 2022
18 changes: 9 additions & 9 deletions src/main/java/io/nats/client/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

import static io.nats.client.support.Encoding.uriDecode;
import static io.nats.client.support.NatsConstants.*;

/**
Expand Down Expand Up @@ -1949,16 +1950,15 @@ public CharBuffer buildProtocolConnectOptionsString(String serverURI, boolean in
// Values from URI override options
try {
URI uri = this.createURIForServer(serverURI);
String userInfo = uri.getUserInfo();

String userInfo = uri.getRawUserInfo();
if (userInfo != null) {
String[] info = userInfo.split(":");

if (info.length == 2) {
uriUser = info[0];
uriPass = info[1];
} else {
uriToken = userInfo;
int at = userInfo.indexOf(":");
caleblloyd marked this conversation as resolved.
Show resolved Hide resolved
if (at == -1) {
uriToken = uriDecode(userInfo);
}
else {
uriUser = uriDecode(userInfo.substring(0, at));
uriPass = uriDecode(userInfo.substring(at + 1));
}
}
} catch(URISyntaxException e) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/nats/client/impl/NatsConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,8 @@ void sendConnect(String serverURI) throws IOException {
try {
ServerInfo info = this.serverInfo.get();
CharBuffer connectOptions = this.options.buildProtocolConnectOptionsString(serverURI, info.isAuthRequired(), info.getNonce());
ByteArrayBuilder bab = new ByteArrayBuilder(OP_CONNECT_SP_LEN + connectOptions.limit())
ByteArrayBuilder bab =
new ByteArrayBuilder(OP_CONNECT_SP_LEN + connectOptions.limit(), ByteArrayBuilder.DEFAULT_OTHER_ALLOCATION, UTF_8)
.append(CONNECT_SP_BYTES).append(connectOptions);
queueInternalOutgoing(new ProtocolMessage(bab));
} catch (Exception exp) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/nats/client/support/Encoding.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

package io.nats.client.support;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
Expand Down Expand Up @@ -200,4 +202,12 @@ public static void jsonEncode(StringBuilder sb, String s) {
}
}
}

public static String uriDecode(String source) {
caleblloyd marked this conversation as resolved.
Show resolved Hide resolved
try {
return URLDecoder.decode(source.replace("+", "%2B"), "UTF-8");
} catch (UnsupportedEncodingException e) {
return source;
}
}
}
40 changes: 40 additions & 0 deletions src/test/java/io/nats/client/AuthTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,46 @@ public void testUserPass() throws Exception {
}
}

@Test
public void testEncodedPassword() throws Exception {
try (NatsTestServer ts = new NatsTestServer("src/test/resources/encoded_pass.conf", false)) {
int port = ts.getPort();
assertEncoded("space%20space", port);
assertEncoded("colon%3Acolon", port);
assertEncoded("colon%3acolon", port); // just making sure lower case hex
assertEncoded("quote%27quote", port);
assertEncoded("slash%2Fslash", port);
assertEncoded("question%3Fquestion", port);
assertEncoded("pound%23pound", port);
assertEncoded("sqleft%5Bsqleft", port);
assertEncoded("sqright%5Dsqright", port);
assertEncoded("at%40at", port);
assertEncoded("bang%21bang", port);
assertEncoded("dollar%24dollar", port);
assertEncoded("amp%26amp", port);
assertEncoded("comma%2Ccomma", port);
assertEncoded("parenleft%28parenleft", port);
assertEncoded("parentright%29parentright", port);
assertEncoded("asterix%2Aasterix", port);
assertEncoded("plus%2Bplus", port);
assertEncoded("semi%3Bsemi", port);
assertEncoded("eq%3Deq", port);
assertEncoded("pct%25pct", port);
assertEncoded("%2b%3a%c2%a1%c2%a2%c2%a3%c2%a4%c2%a5%c2%a6%c2%a7%c2%a8%c2%a9%c2%aa%c2%ab%c2%ac%20%f0%9f%98%80", port);

// a plus sign in a user or pass is a plus sign, not a space
assertThrows(AuthenticationException.class, () -> assertEncoded("space+space", port));
}
}

private void assertEncoded(String encoded, int port) throws IOException, InterruptedException {
String url = "nats://u" + encoded + ":p" + encoded + "@localhost:" + port;
Options options = new Options.Builder().server(url).build();
Connection c = Nats.connect(options);
c.getServerInfo();
c.close();
}

@Test
public void testUserPassOnReconnect() throws Exception {
TestHandler handler = new TestHandler();
Expand Down
37 changes: 37 additions & 0 deletions src/test/resources/encoded_pass.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
authorization {
ENC = {
publish: {
allow: [
"sub.>"
]
}
subscribe: {
allow: [
"sub.>"
]
}
}
users = [
{user: 'uspace space', password: 'pspace space', permissions: $ENC}
{user: "uquote'quote", password: "pquote'quote", permissions: $ENC}
{user: "ucolon:colon", password: "pcolon:colon", permissions: $ENC}
{user: 'uslash/slash', password: 'pslash/slash', permissions: $ENC}
{user: 'uquestion?question', password: 'pquestion?question', permissions: $ENC}
{user: 'upound#pound', password: 'ppound#pound', permissions: $ENC}
{user: 'usqleft[sqleft', password: 'psqleft[sqleft', permissions: $ENC}
{user: 'usqright]sqright', password: 'psqright]sqright', permissions: $ENC}
{user: 'uat@at', password: 'pat@at', permissions: $ENC}
{user: 'ubang!bang', password: 'pbang!bang', permissions: $ENC}
{user: 'udollar$dollar', password: 'pdollar$dollar', permissions: $ENC}
{user: 'uamp&amp', password: 'pamp&amp', permissions: $ENC}
{user: 'ucomma,comma', password: 'pcomma,comma', permissions: $ENC}
{user: 'uparenleft(parenleft', password: 'pparenleft(parenleft', permissions: $ENC}
{user: 'uparentright)parentright', password: 'pparentright)parentright', permissions: $ENC}
{user: 'uasterix*asterix', password: 'pasterix*asterix', permissions: $ENC}
{user: 'uplus+plus', password: 'pplus+plus', permissions: $ENC}
{user: 'usemi;semi', password: 'psemi;semi', permissions: $ENC}
{user: 'ueq=eq', password: 'peq=eq', permissions: $ENC}
{user: 'upct%pct', password: 'ppct%pct', permissions: $ENC}
{user: "u+:¡¢£¤¥¦§¨©ª«¬ 😀", password: "p+:¡¢£¤¥¦§¨©ª«¬ 😀", permissions: $ENC}
]
}