From 95a6cc8afa010bfbb113969cd4b388978b7492e0 Mon Sep 17 00:00:00 2001 From: Sreenath Madasu Date: Sat, 8 Mar 2025 16:22:49 -0500 Subject: [PATCH 1/8] Fix for bug #4003. Better message instead of ArraIndexOutOfBoundsException --- .../redis/clients/jedis/util/JedisURIHelper.java | 7 ++++++- .../clients/jedis/util/JedisURIHelperTest.java | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java index a565d38048..79ecba6ae1 100644 --- a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java +++ b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java @@ -4,6 +4,7 @@ import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.exceptions.JedisException; public final class JedisURIHelper { @@ -33,7 +34,11 @@ public static String getUser(URI uri) { public static String getPassword(URI uri) { String userInfo = uri.getUserInfo(); if (userInfo != null) { - return userInfo.split(":", 2)[1]; + String[] userAndPassword = userInfo.split(":", 2); + if (userAndPassword.length < 2) { + throw new JedisException("AUTH error. Password not provided in uri"); + } + return userAndPassword[1]; } return null; } diff --git a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java index be4fded987..b193be8daa 100644 --- a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java +++ b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java @@ -1,15 +1,14 @@ package redis.clients.jedis.util; +import static org.junit.Assert.*; import static redis.clients.jedis.util.JedisURIHelper.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; - import java.net.URI; import java.net.URISyntaxException; import org.junit.Test; +import org.junit.rules.ExpectedException; import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.exceptions.JedisException; public class JedisURIHelperTest { @@ -73,4 +72,13 @@ public void shouldGetProtocolFromDefinition() { assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1?protocol=3"))); assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3"))); } + + @Test + public void shouldReturnNullIfURIDoesNotHavePassword() throws URISyntaxException { + URI uri = new URI("redis://user@host:9000/0"); + JedisException jedisException = assertThrows(JedisException.class, () -> { + getPassword(uri); + }); + assertEquals(jedisException.getMessage(), "AUTH error. Password not provided in uri"); + } } From add343ce90c4854c8f43bb97f8b8f73450d85833 Mon Sep 17 00:00:00 2001 From: Sreenath Madasu Date: Sun, 9 Mar 2025 12:35:09 -0400 Subject: [PATCH 2/8] Update src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java Co-authored-by: ggivo --- src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java index b193be8daa..e9c77acde1 100644 --- a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java +++ b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java @@ -8,7 +8,8 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import redis.clients.jedis.RedisProtocol; -import redis.clients.jedis.exceptions.JedisException; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.emptyString; public class JedisURIHelperTest { From 6968e913ef18d7b10b07a743f7576242d8c96c33 Mon Sep 17 00:00:00 2001 From: Sreenath Madasu Date: Sun, 9 Mar 2025 12:35:15 -0400 Subject: [PATCH 3/8] Update src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java Co-authored-by: ggivo --- .../redis/clients/jedis/util/JedisURIHelperTest.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java index e9c77acde1..d687cb09f8 100644 --- a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java +++ b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java @@ -73,13 +73,19 @@ public void shouldGetProtocolFromDefinition() { assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1?protocol=3"))); assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3"))); } + @Test + public void shouldReturnEmptyPassword() { + // ensure we can provide an empty password for default user + assertThat( JedisURIHelper.getPassword(URI.create("redis://:@host:9000/0")), emptyString()); + } @Test - public void shouldReturnNullIfURIDoesNotHavePassword() throws URISyntaxException { + public void shouldThrowIfNoPasswordInURI() throws URISyntaxException { + // ensure we throw if user is provided but password is missing in URI URI uri = new URI("redis://user@host:9000/0"); - JedisException jedisException = assertThrows(JedisException.class, () -> { + IllegalArgumentException jedisException = assertThrows(IllegalArgumentException.class, () -> { getPassword(uri); }); - assertEquals(jedisException.getMessage(), "AUTH error. Password not provided in uri"); + assertEquals(jedisException.getMessage(), "Password not provided in the URI"); } } From c94d55cb843a03b35c6edeecae2053105ac027ea Mon Sep 17 00:00:00 2001 From: Sreenath Madasu Date: Sun, 9 Mar 2025 12:39:28 -0400 Subject: [PATCH 4/8] Fix for bug #4003. Code review suggestions --- src/main/java/redis/clients/jedis/util/JedisURIHelper.java | 2 +- .../java/redis/clients/jedis/util/JedisURIHelperTest.java | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java index 79ecba6ae1..a649443e63 100644 --- a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java +++ b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java @@ -36,7 +36,7 @@ public static String getPassword(URI uri) { if (userInfo != null) { String[] userAndPassword = userInfo.split(":", 2); if (userAndPassword.length < 2) { - throw new JedisException("AUTH error. Password not provided in uri"); + throw new IllegalArgumentException("AUTH error. Password not provided in uri"); } return userAndPassword[1]; } diff --git a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java index d687cb09f8..0620573d2e 100644 --- a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java +++ b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java @@ -6,7 +6,6 @@ import java.net.URI; import java.net.URISyntaxException; import org.junit.Test; -import org.junit.rules.ExpectedException; import redis.clients.jedis.RedisProtocol; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.emptyString; @@ -83,9 +82,7 @@ public void shouldReturnEmptyPassword() { public void shouldThrowIfNoPasswordInURI() throws URISyntaxException { // ensure we throw if user is provided but password is missing in URI URI uri = new URI("redis://user@host:9000/0"); - IllegalArgumentException jedisException = assertThrows(IllegalArgumentException.class, () -> { - getPassword(uri); - }); - assertEquals(jedisException.getMessage(), "Password not provided in the URI"); + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, () -> getPassword(uri)); + assertEquals("AUTH error. Password not provided in uri", illegalArgumentException.getMessage()); } } From c773f45a6c9069a2c31348f698cb7871c83ebc06 Mon Sep 17 00:00:00 2001 From: ggivo Date: Sun, 9 Mar 2025 22:32:11 +0200 Subject: [PATCH 5/8] Add javadoc + formatting --- .../clients/jedis/util/JedisURIHelper.java | 45 ++++++++++++++++++- .../jedis/util/JedisURIHelperTest.java | 6 ++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java index a649443e63..29093f5eab 100644 --- a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java +++ b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java @@ -4,8 +4,31 @@ import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; -import redis.clients.jedis.exceptions.JedisException; +/** + * Utility class for handling Redis URIs. + * This class provides methods to extract various components from a Redis URI, + * such as host, port, user, password, database index, and protocol. + * It also includes methods to validate the URI and check its scheme. + * + *

URI syntax

+ * + *
redis[s]{@code ://}[[username][{@code :}password@]]host + * [{@code :}port][{@code /}database] + *
+ * + * + *

Authentication

+ *

Authentication details can be provided in the URI in the form of a username and password. + * Redis URIs may contain authentication details that effectively lead to usernames with passwords, + * password-only, or no authentication. + *

Examples:

+ *
    + *
  • Username and Password: redis://username:password@host:port
  • + *
  • Password-only: redis://:password@host:port
  • + *
  • No Authentication: redis://host:port
  • + *
* + */ public final class JedisURIHelper { private static final String REDIS = "redis"; @@ -19,6 +42,16 @@ public static HostAndPort getHostAndPort(URI uri) { return new HostAndPort(uri.getHost(), uri.getPort()); } + + /** + * Extracts the user from the given URI. + * + *

For details on the URI format and authentication examples, see the class-level documentation + * {@link JedisURIHelper JedisURIHelper}.

+ * + * @param uri the URI to extract the user from + * @return the user as a String, or null if {@link URI#getUserInfo()} info is missing + */ public static String getUser(URI uri) { String userInfo = uri.getUserInfo(); if (userInfo != null) { @@ -31,6 +64,16 @@ public static String getUser(URI uri) { return null; } + /** + * Extracts the password from the given URI. + * + *

For details on the URI format and authentication examples, see the class-level documentation + * {@link JedisURIHelper JedisURIHelper}.

+ * + * @param uri the URI to extract the password from + * @return the password as a String, or null if {@link URI#getUserInfo()} info is missing + * @throws IllegalArgumentException if {@link URI#getUserInfo()} is provided but does not contain a password (i.e., the URI does not contain a colon) + */ public static String getPassword(URI uri) { String userInfo = uri.getUserInfo(); if (userInfo != null) { diff --git a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java index 0620573d2e..bebbcf987d 100644 --- a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java +++ b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java @@ -72,17 +72,19 @@ public void shouldGetProtocolFromDefinition() { assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1?protocol=3"))); assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3"))); } + @Test public void shouldReturnEmptyPassword() { // ensure we can provide an empty password for default user - assertThat( JedisURIHelper.getPassword(URI.create("redis://:@host:9000/0")), emptyString()); + assertThat(JedisURIHelper.getPassword(URI.create("redis://:@host:9000/0")), emptyString()); } @Test public void shouldThrowIfNoPasswordInURI() throws URISyntaxException { // ensure we throw if user is provided but password is missing in URI URI uri = new URI("redis://user@host:9000/0"); - IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, () -> getPassword(uri)); + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> getPassword(uri)); assertEquals("AUTH error. Password not provided in uri", illegalArgumentException.getMessage()); } } From e7cb06beab708e98bd75cc5e4faff7f34b0ddbcd Mon Sep 17 00:00:00 2001 From: ggivo Date: Sun, 9 Mar 2025 22:59:36 +0200 Subject: [PATCH 6/8] [clean-up] updated javadoc for empty password & exception message. --- .../java/redis/clients/jedis/util/JedisURIHelper.java | 5 +++-- .../java/redis/clients/jedis/util/JedisURIHelperTest.java | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java index 29093f5eab..d80d7b8b73 100644 --- a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java +++ b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java @@ -26,7 +26,8 @@ *
    *
  • 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
  • + *
  • No Authentication: redis://host:port
  • \ *
* */ public final class JedisURIHelper { @@ -79,7 +80,7 @@ public static String getPassword(URI uri) { if (userInfo != null) { String[] userAndPassword = userInfo.split(":", 2); if (userAndPassword.length < 2) { - throw new IllegalArgumentException("AUTH error. Password not provided in uri"); + throw new IllegalArgumentException("Password not provided in uri."); } return userAndPassword[1]; } diff --git a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java index bebbcf987d..9be71f2f90 100644 --- a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java +++ b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java @@ -74,9 +74,13 @@ public void shouldGetProtocolFromDefinition() { } @Test - public void shouldReturnEmptyPassword() { + public void emptyPassword() { // ensure we can provide an empty password for default user assertThat(JedisURIHelper.getPassword(URI.create("redis://:@host:9000/0")), emptyString()); + + // ensure we can provide an empty password for user + assertEquals(JedisURIHelper.getUser(URI.create("redis://username:@host:9000/0")), "username"); + assertThat(JedisURIHelper.getPassword(URI.create("redis://username:@host:9000/0")), emptyString()); } @Test @@ -85,6 +89,6 @@ public void shouldThrowIfNoPasswordInURI() throws URISyntaxException { URI uri = new URI("redis://user@host:9000/0"); IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, () -> getPassword(uri)); - assertEquals("AUTH error. Password not provided in uri", illegalArgumentException.getMessage()); + assertEquals("Password not provided in uri.", illegalArgumentException.getMessage()); } } From 0d96a9e6dd846d85a733ad52df3181f621950df9 Mon Sep 17 00:00:00 2001 From: ggivo Date: Sun, 9 Mar 2025 23:33:01 +0200 Subject: [PATCH 7/8] [clean-up] formating --- .../clients/jedis/util/JedisURIHelper.java | 25 +++++++++---------- .../jedis/util/JedisURIHelperTest.java | 14 +++++++---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java index d80d7b8b73..07c4c0e9fc 100644 --- a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java +++ b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java @@ -13,8 +13,9 @@ * *

URI syntax

* - *
redis[s]{@code ://}[[username][{@code :}password@]]host - * [{@code :}port][{@code /}database] + *
+ * redis[s]{@code ://}[[username][{@code :}password]@] + * host[{@code :}port][{@code /}database] *
* * @@ -43,15 +44,13 @@ public static HostAndPort getHostAndPort(URI uri) { return new HostAndPort(uri.getHost(), uri.getPort()); } - /** * Extracts the user from the given URI. - * - *

For details on the URI format and authentication examples, see the class-level documentation - * {@link JedisURIHelper JedisURIHelper}.

- * + *

+ * For details on the URI format and authentication examples, see {@link JedisURIHelper}. + *

* @param uri the URI to extract the user from - * @return the user as a String, or null if {@link URI#getUserInfo()} info is missing + * @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(); @@ -67,13 +66,13 @@ public static String getUser(URI uri) { /** * Extracts the password from the given URI. - * - *

For details on the URI format and authentication examples, see the class-level documentation - * {@link JedisURIHelper JedisURIHelper}.

- * + *

+ * For details on the URI format and authentication examples, see {@link JedisURIHelper}. + *

* @param uri the URI to extract the password from * @return the password as a String, or null if {@link URI#getUserInfo()} info is missing - * @throws IllegalArgumentException if {@link URI#getUserInfo()} is provided but does not contain a password (i.e., the URI does not contain a colon) + * @throws IllegalArgumentException if {@link URI#getUserInfo()} is provided but does not contain + * a password */ public static String getPassword(URI uri) { String userInfo = uri.getUserInfo(); diff --git a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java index 9be71f2f90..9b424f1164 100644 --- a/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java +++ b/src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java @@ -68,9 +68,12 @@ public void shouldGetDefaultProtocolWhenNotDefined() { @Test public void shouldGetProtocolFromDefinition() { assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234?protocol=3"))); - assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/?protocol=3"))); - assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1?protocol=3"))); - assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3"))); + assertEquals(RedisProtocol.RESP3, + getRedisProtocol(URI.create("redis://host:1234/?protocol=3"))); + assertEquals(RedisProtocol.RESP3, + getRedisProtocol(URI.create("redis://host:1234/1?protocol=3"))); + assertEquals(RedisProtocol.RESP3, + getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3"))); } @Test @@ -80,7 +83,8 @@ public void emptyPassword() { // ensure we can provide an empty password for user assertEquals(JedisURIHelper.getUser(URI.create("redis://username:@host:9000/0")), "username"); - assertThat(JedisURIHelper.getPassword(URI.create("redis://username:@host:9000/0")), emptyString()); + assertThat(JedisURIHelper.getPassword(URI.create("redis://username:@host:9000/0")), + emptyString()); } @Test @@ -88,7 +92,7 @@ public void shouldThrowIfNoPasswordInURI() throws URISyntaxException { // ensure we throw if user is provided but password is missing in URI URI uri = new URI("redis://user@host:9000/0"); IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, - () -> getPassword(uri)); + () -> getPassword(uri)); assertEquals("Password not provided in uri.", illegalArgumentException.getMessage()); } } From 87dcc51443ba4f1733b014af2d0421e49c64d029 Mon Sep 17 00:00:00 2001 From: ggivo Date: Sun, 9 Mar 2025 23:57:02 +0200 Subject: [PATCH 8/8] [clean-up] fix javadoc syntax errors --- .../redis/clients/jedis/util/JedisURIHelper.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java index 07c4c0e9fc..e108eaafa8 100644 --- a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java +++ b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java @@ -11,7 +11,7 @@ * such as host, port, user, password, database index, and protocol. * It also includes methods to validate the URI and check its scheme. * - *

URI syntax

+ *

URI syntax

* *
* redis[s]{@code ://}[[username][{@code :}password]@] @@ -19,17 +19,17 @@ *
* * - *

Authentication

+ *

Authentication

*

Authentication details can be provided in the URI in the form of a username and password. * Redis URIs may contain authentication details that effectively lead to usernames with passwords, - * password-only, or no authentication. - *

Examples:

+ * password-only, or no authentication.

+ *

Examples:

*
    *
  • Username and Password: redis://username:password@host:port
  • *
  • Password-only: redis://:password@host:port
  • *
  • Empty password: redis://username:@host:port
  • - *
  • No Authentication: redis://host:port
  • \ - *
* + *
  • No Authentication: redis://host:port
  • + * */ public final class JedisURIHelper {