Skip to content

Commit c3f5304

Browse files
smadasuggivo
andauthored
Fix JedisURIHelper.getPassword throwing ArrayIndexOutOfBoundsException for username-only URIs (#4109)
Fix for bug #4003. Better message instead of ArrayIndexOutOfBoundsException Authentication details can be provided in the URI in the form of a username and password. Examples : - Username and Password: redis://username:password@host:port - Password-only: redis://:password@host:port - No Authentication: redis://host:port - Empty password: redis://username:@host:port Using JedisURIHelper with a URI having only a username is considered invalid and should throw `IllegalArgumentException`. Closes #4003. --------- Co-authored-by: ggivo <[email protected]>
1 parent d34c007 commit c3f5304

File tree

2 files changed

+78
-8
lines changed

2 files changed

+78
-8
lines changed

src/main/java/redis/clients/jedis/util/JedisURIHelper.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@
55
import redis.clients.jedis.Protocol;
66
import redis.clients.jedis.RedisProtocol;
77

8+
/**
9+
* Utility class for handling Redis URIs.
10+
* This class provides methods to extract various components from a Redis URI,
11+
* such as host, port, user, password, database index, and protocol.
12+
* It also includes methods to validate the URI and check its scheme.
13+
*
14+
* <h2>URI syntax</h2>
15+
*
16+
* <blockquote>
17+
* <i>redis[s]</i><b>{@code ://}</b>[[<i>username</i>]<i>[{@code :}password</i>]@]
18+
* <i>host</i>[<b>{@code :}</b><i>port</i>][<b>{@code /}</b><i>database</i>]
19+
* </blockquote>
20+
*
21+
*
22+
* <h2>Authentication</h2>
23+
* <p>Authentication details can be provided in the URI in the form of a username and password.
24+
* Redis URIs may contain authentication details that effectively lead to usernames with passwords,
25+
* password-only, or no authentication.</p>
26+
* <h3>Examples:</h3>
27+
* <ul>
28+
* <li><b>Username and Password:</b> redis://username:password@host:port</li>
29+
* <li><b>Password-only:</b> redis://:password@host:port</li>
30+
* <li><b>Empty password:</b> redis://username:@host:port</li>
31+
* <li><b>No Authentication:</b> redis://host:port</li>
32+
* </ul>
33+
*/
834
public final class JedisURIHelper {
935

1036
private static final String REDIS = "redis";
@@ -18,6 +44,14 @@ public static HostAndPort getHostAndPort(URI uri) {
1844
return new HostAndPort(uri.getHost(), uri.getPort());
1945
}
2046

47+
/**
48+
* Extracts the user from the given URI.
49+
* <p>
50+
* For details on the URI format and authentication examples, see {@link JedisURIHelper}.
51+
* </p>
52+
* @param uri the URI to extract the user from
53+
* @return the user as a String, or null if user is empty or {@link URI#getUserInfo()} info is missing
54+
*/
2155
public static String getUser(URI uri) {
2256
String userInfo = uri.getUserInfo();
2357
if (userInfo != null) {
@@ -30,10 +64,24 @@ public static String getUser(URI uri) {
3064
return null;
3165
}
3266

67+
/**
68+
* Extracts the password from the given URI.
69+
* <p>
70+
* For details on the URI format and authentication examples, see {@link JedisURIHelper}.
71+
* </p>
72+
* @param uri the URI to extract the password from
73+
* @return the password as a String, or null if {@link URI#getUserInfo()} info is missing
74+
* @throws IllegalArgumentException if {@link URI#getUserInfo()} is provided but does not contain
75+
* a password
76+
*/
3377
public static String getPassword(URI uri) {
3478
String userInfo = uri.getUserInfo();
3579
if (userInfo != null) {
36-
return userInfo.split(":", 2)[1];
80+
String[] userAndPassword = userInfo.split(":", 2);
81+
if (userAndPassword.length < 2) {
82+
throw new IllegalArgumentException("Password not provided in uri.");
83+
}
84+
return userAndPassword[1];
3785
}
3886
return null;
3987
}

src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package redis.clients.jedis.util;
22

3+
import static org.junit.Assert.*;
34
import static redis.clients.jedis.util.JedisURIHelper.*;
45

5-
import static org.junit.Assert.assertEquals;
6-
import static org.junit.Assert.assertFalse;
7-
import static org.junit.Assert.assertNull;
8-
96
import java.net.URI;
107
import java.net.URISyntaxException;
118
import org.junit.Test;
129
import redis.clients.jedis.RedisProtocol;
10+
import static org.hamcrest.MatcherAssert.assertThat;
11+
import static org.hamcrest.Matchers.emptyString;
1312

1413
public class JedisURIHelperTest {
1514

@@ -69,8 +68,31 @@ public void shouldGetDefaultProtocolWhenNotDefined() {
6968
@Test
7069
public void shouldGetProtocolFromDefinition() {
7170
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234?protocol=3")));
72-
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/?protocol=3")));
73-
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1?protocol=3")));
74-
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3")));
71+
assertEquals(RedisProtocol.RESP3,
72+
getRedisProtocol(URI.create("redis://host:1234/?protocol=3")));
73+
assertEquals(RedisProtocol.RESP3,
74+
getRedisProtocol(URI.create("redis://host:1234/1?protocol=3")));
75+
assertEquals(RedisProtocol.RESP3,
76+
getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3")));
77+
}
78+
79+
@Test
80+
public void emptyPassword() {
81+
// ensure we can provide an empty password for default user
82+
assertThat(JedisURIHelper.getPassword(URI.create("redis://:@host:9000/0")), emptyString());
83+
84+
// ensure we can provide an empty password for user
85+
assertEquals(JedisURIHelper.getUser(URI.create("redis://username:@host:9000/0")), "username");
86+
assertThat(JedisURIHelper.getPassword(URI.create("redis://username:@host:9000/0")),
87+
emptyString());
88+
}
89+
90+
@Test
91+
public void shouldThrowIfNoPasswordInURI() throws URISyntaxException {
92+
// ensure we throw if user is provided but password is missing in URI
93+
URI uri = new URI("redis://user@host:9000/0");
94+
IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class,
95+
() -> getPassword(uri));
96+
assertEquals("Password not provided in uri.", illegalArgumentException.getMessage());
7597
}
7698
}

0 commit comments

Comments
 (0)