Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions src/main/java/redis/clients/jedis/util/JedisURIHelper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package redis.clients.jedis.util;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.RedisProtocol;
Expand Down Expand Up @@ -53,9 +56,9 @@ public static HostAndPort getHostAndPort(URI uri) {
* @return the user as a String, or null if user is empty or {@link URI#getUserInfo()} info is missing
*/
public static String getUser(URI uri) {
String userInfo = uri.getUserInfo();
String userInfo = uri.getRawUserInfo();
if (userInfo != null) {
String user = userInfo.split(":", 2)[0];
String user = decode(userInfo.split(":", 2)[0]);
if (user.isEmpty()) {
user = null; // return null user is not specified
}
Expand All @@ -75,17 +78,34 @@ public static String getUser(URI uri) {
* a password
*/
public static String getPassword(URI uri) {
String userInfo = uri.getUserInfo();
String userInfo = uri.getRawUserInfo();
if (userInfo != null) {
String[] userAndPassword = userInfo.split(":", 2);
if (userAndPassword.length < 2) {
throw new IllegalArgumentException("Password not provided in uri.");
}
return userAndPassword[1];
return decode(userAndPassword[1]);
}
return null;
}

/**
* Percent-decodes a single component of the raw userinfo. {@link URI#getUserInfo()} cannot be
* used before the {@code ':'} split, because it decodes first: a percent-encoded {@code ':'}
* ({@code %3A}) inside the username becomes a literal {@code ':'} and shifts the split point, so
* {@code redis://us%3Aer:pw@host} is read as user {@code us} with password {@code er:pw} instead
* of user {@code us:er} with password {@code pw}. Splitting the raw userinfo keeps the boundary
* intact and each half is decoded here. {@code '+'} is a literal in userinfo (not a space as in
* form data), so it is guarded from {@link URLDecoder}.
*/
private static String decode(String value) {
try {
return URLDecoder.decode(value.replace("+", "%2B"), StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e); // UTF-8 is always supported
}
}

/**
* Checks if the given URI has a database index component.
*
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ public void shouldGetNullUserFromURIWithCredentials() throws URISyntaxException
assertEquals("password", JedisURIHelper.getPassword(uri));
}

@Test
public void shouldKeepEncodedColonInsideUsername() throws URISyntaxException {
// %3A in the username must not move the user:password split point
URI uri = new URI("redis://us%3Aer:pw@host:9000/0");
assertEquals("us:er", JedisURIHelper.getUser(uri));
assertEquals("pw", JedisURIHelper.getPassword(uri));
}

@Test
public void shouldDecodeEncodedCredentials() throws URISyntaxException {
// percent-encoded specials in the password decode, literal '+' stays literal
assertEquals("pa@ss", JedisURIHelper.getPassword(new URI("redis://user:pa%40ss@host:9000/0")));
assertEquals("pa:ss", JedisURIHelper.getPassword(new URI("redis://user:pa%3Ass@host:9000/0")));
assertEquals("pa+ss", JedisURIHelper.getPassword(new URI("redis://user:pa+ss@host:9000/0")));
}

@Test
public void shouldReturnNullIfURIDoesNotHaveCredentials() throws URISyntaxException {
URI uri = new URI("redis://host:9000/0");
Expand Down
Loading