diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index dd436ead6f..c3a64234aa 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -95,6 +95,11 @@ public RedisFuture aclDeluser(String... usernames) { return dispatch(commandBuilder.aclDeluser(usernames)); } + @Override + public RedisFuture aclDryRun(String username, String command, V... args) { + return dispatch(commandBuilder.aclDryRun(username, command, args)); + } + @Override public RedisFuture aclGenpass() { return dispatch(commandBuilder.aclGenpass()); diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 850ef29853..a0d2fc8df4 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -127,6 +127,11 @@ public Mono aclDeluser(String... usernames) { return createMono(() -> commandBuilder.aclDeluser(usernames)); } + @Override + public Mono aclDryRun(String username, String command, V... args) { + return createMono(() -> commandBuilder.aclDryRun(username, command, args)); + } + @Override public Mono aclGenpass() { return createMono(commandBuilder::aclGenpass); diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java index 64aa98afef..5003250298 100644 --- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java @@ -93,6 +93,15 @@ Command aclDeluser(String... usernames) { return createCommand(ACL, new IntegerOutput<>(codec), args); } + Command aclDryRun(String username, String command, V... commandArgs) { + LettuceAssert.notNull(username, "username " + MUST_NOT_BE_NULL); + LettuceAssert.notNull(command, "command " + MUST_NOT_BE_NULL); + + CommandArgs args = new CommandArgs<>(codec); + args.add(DRYRUN).add(username).add(command).addValues(commandArgs); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + Command aclGenpass() { CommandArgs args = new CommandArgs<>(codec); args.add(GENPASS); diff --git a/src/main/java/io/lettuce/core/api/async/RedisAclAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisAclAsyncCommands.java index d970a33b31..9e6eb2217b 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisAclAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisAclAsyncCommands.java @@ -56,6 +56,17 @@ public interface RedisAclAsyncCommands { */ RedisFuture aclDeluser(String... usernames); + /** + * Simulate the execution of a given command by a given user. + * + * @param username the specified username + * @param command the specified command + * @param args the specified args of command + * @return String reply: OK on success. + * @since 6.2 + */ + RedisFuture aclDryRun(String username, String command, V... args); + /** * The command generates a password. * diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisAclReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisAclReactiveCommands.java index ae4d3cc1ed..92bfd56dc1 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisAclReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisAclReactiveCommands.java @@ -57,6 +57,17 @@ public interface RedisAclReactiveCommands { */ Mono aclDeluser(String... usernames); + /** + * Simulate the execution of a given command by a given user. + * + * @param username the specified username + * @param command the specified command + * @param args the specified args of command + * @return String reply: OK on success. + * @since 6.2 + */ + Mono aclDryRun(String username, String command, V... args); + /** * The command generates a password. * diff --git a/src/main/java/io/lettuce/core/api/sync/RedisAclCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisAclCommands.java index badadb3e92..57d0b397b8 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisAclCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisAclCommands.java @@ -55,6 +55,17 @@ public interface RedisAclCommands { */ Long aclDeluser(String... usernames); + /** + * Simulate the execution of a given command by a given user. + * + * @param username the specified username + * @param command the specified command + * @param args the specified args of command + * @return String reply: OK on success. + * @since 6.2 + */ + String aclDryRun(String username, String command, V... args); + /** * The command generates a password. * diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAclAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAclAsyncCommands.java index 4a1a6158e6..b767daa8f2 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAclAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAclAsyncCommands.java @@ -55,6 +55,17 @@ public interface NodeSelectionAclAsyncCommands { */ AsyncExecutions aclDeluser(String... usernames); + /** + * Simulate the execution of a given command by a given user. + * + * @param username the specified username + * @param command the specified command + * @param args the specified args of command + * @return String reply: OK on success. + * @since 6.2 + */ + AsyncExecutions aclDryRun(String username, String command, V... args); + /** * The command generates a password. * diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionAclCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionAclCommands.java index b1ae4d02d5..743409550c 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionAclCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionAclCommands.java @@ -55,6 +55,17 @@ public interface NodeSelectionAclCommands { */ Executions aclDeluser(String... usernames); + /** + * Simulate the execution of a given command by a given user. + * + * @param username the specified username + * @param command the specified command + * @param args the specified args of command + * @return String reply: OK on success. + * @since 6.2 + */ + Executions aclDryRun(String username, String command, V... args); + /** * The command generates a password. * diff --git a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java index 691bf0732a..ce113f7a42 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java +++ b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java @@ -29,7 +29,7 @@ public enum CommandKeyword implements ProtocolKeyword { ABSTTL, ADDR, ADDSLOTS, ADDSLOTSRANGE, AFTER, AGGREGATE, ALLCHANNELS, ALLCOMMANDS, ALLKEYS, ALPHA, AND, ASK, ASC, ASYNC, BEFORE, BLOCK, BUMPEPOCH, - BY, BYLEX, BYSCORE, CACHING, CAT, CH, CHANNELS, COPY, COUNT, COUNTKEYSINSLOT, CONSUMERS, CREATE, DB, DELSLOTS, DELSLOTSRANGE, DELUSER, DESC, SOFT, HARD, ENCODING, + BY, BYLEX, BYSCORE, CACHING, CAT, CH, CHANNELS, COPY, COUNT, COUNTKEYSINSLOT, CONSUMERS, CREATE, DB, DELSLOTS, DELSLOTSRANGE, DELUSER, DESC, DRYRUN, SOFT, HARD, ENCODING, FAILOVER, FORGET, FLUSH, FORCE, FREQ, FLUSHSLOTS, GENPASS, GETNAME, GETUSER, GETKEYSINSLOT, GETREDIR, GROUP, GROUPS, HTSTATS, ID, IDLE, diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommands.kt index ade7f32775..db6f18bcc7 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommands.kt @@ -55,6 +55,17 @@ interface RedisAclCoroutinesCommands { */ suspend fun aclDeluser(vararg usernames: String): Long? + /** + * Simulate the execution of a given command by a given user. + * + * @param username the specified username + * @param command the specified command + * @param args the specified args of command + * @return String reply: OK on success. + * @since 6.2 + */ + suspend fun aclDryRun(username: String, command: String, vararg args: V): String? + /** * The command generates a password. * diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommandsImpl.kt index 22adc54067..bbfee02089 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommandsImpl.kt @@ -47,6 +47,8 @@ internal class RedisAclCoroutinesCommandsImpl(internal val ops override suspend fun aclDeluser(vararg usernames: String): Long? = ops.aclDeluser(*usernames).awaitFirstOrNull() + override suspend fun aclDryRun(username: String, command: String, vararg args: V): String? = ops.aclDryRun(username, command, *args).awaitFirstOrNull() + override suspend fun aclGenpass(): String? = ops.aclGenpass().awaitFirstOrNull() override suspend fun aclGenpass(bits: Int): String? = ops.aclGenpass(bits).awaitFirstOrNull() diff --git a/src/main/templates/io/lettuce/core/api/RedisAclCommands.java b/src/main/templates/io/lettuce/core/api/RedisAclCommands.java index 7531832457..32c3ce1336 100644 --- a/src/main/templates/io/lettuce/core/api/RedisAclCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisAclCommands.java @@ -53,6 +53,17 @@ public interface RedisAclCommands { */ Long aclDeluser(String... usernames); + /** + * Simulate the execution of a given command by a given user. + * + * @param username the specified username + * @param command the specified command + * @param args the specified args of command + * @return String reply: OK on success. + * @since 6.2 + */ + String aclDryRun(String username, String command, V... args); + /** * The command generates a password. * diff --git a/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java index 0b117f8554..0ae81f015a 100644 --- a/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java @@ -69,6 +69,14 @@ void aclDeluser() { assertThat(redis.aclDeluser("non-existing")).isZero(); } + @Test + @EnabledOnCommand("EVAL_RO") // Redis 7.0 + void aclDryRun() { + assertThatThrownBy(() -> redis.aclDryRun("non-existing", "GET", "foo", "bar")) + .isInstanceOf(RedisCommandExecutionException.class).hasMessageContaining("ERR User 'non-existing' not found"); + assertThat(redis.aclDryRun("default", "GET", "foo", "bar")).isEqualTo("OK"); + } + @Test void aclGenpass() { assertThat(redis.aclGenpass()).hasSize(64);