diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index 809056d4e8..7f8ac08ce2 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -57,11 +57,12 @@ * @author Andrey Shlykov */ @SuppressWarnings("unchecked") -public abstract class AbstractRedisAsyncCommands implements RedisHashAsyncCommands, RedisKeyAsyncCommands, - RedisStringAsyncCommands, RedisListAsyncCommands, RedisSetAsyncCommands, - RedisSortedSetAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands, - RedisHLLAsyncCommands, BaseRedisAsyncCommands, RedisTransactionalAsyncCommands, - RedisGeoAsyncCommands, RedisClusterAsyncCommands { +public abstract class AbstractRedisAsyncCommands implements RedisAclAsyncCommands, + RedisHashAsyncCommands, RedisKeyAsyncCommands, RedisStringAsyncCommands, + RedisListAsyncCommands, RedisSetAsyncCommands, RedisSortedSetAsyncCommands, + RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisHLLAsyncCommands, + BaseRedisAsyncCommands, RedisTransactionalAsyncCommands, RedisGeoAsyncCommands, + RedisClusterAsyncCommands { private final StatefulConnection connection; @@ -78,6 +79,81 @@ public AbstractRedisAsyncCommands(StatefulConnection connection, RedisCode this.commandBuilder = new RedisCommandBuilder<>(codec); } + @Override + public RedisFuture> aclCat() { + return dispatch(commandBuilder.aclCat()); + } + + @Override + public RedisFuture> aclCat(AclCategory category) { + return dispatch(commandBuilder.aclCat(category)); + } + + @Override + public RedisFuture aclDeluser(String... usernames) { + return dispatch(commandBuilder.aclDeluser(usernames)); + } + + @Override + public RedisFuture aclGenpass() { + return dispatch(commandBuilder.aclGenpass()); + } + + @Override + public RedisFuture aclGenpass(int bits) { + return dispatch(commandBuilder.aclGenpass(bits)); + } + + @Override + public RedisFuture> aclGetuser(String username) { + return dispatch(commandBuilder.aclGetuser(username)); + } + + @Override + public RedisFuture> aclList() { + return dispatch(commandBuilder.aclList()); + } + + @Override + public RedisFuture aclLoad() { + return dispatch(commandBuilder.aclLoad()); + } + + @Override + public RedisFuture>> aclLog() { + return dispatch(commandBuilder.aclLog()); + } + + @Override + public RedisFuture>> aclLog(int count) { + return dispatch(commandBuilder.aclLog(count)); + } + + @Override + public RedisFuture aclLogReset() { + return dispatch(commandBuilder.aclLogReset()); + } + + @Override + public RedisFuture aclSave() { + return dispatch(commandBuilder.aclSave()); + } + + @Override + public RedisFuture aclSetuser(String username, AclSetuserArgs args) { + return dispatch(commandBuilder.aclSetuser(username, args)); + } + + @Override + public RedisFuture> aclUsers() { + return dispatch(commandBuilder.aclUsers()); + } + + @Override + public RedisFuture aclWhoami() { + return dispatch(commandBuilder.aclWhoami()); + } + @Override public RedisFuture append(K key, V value) { return dispatch(commandBuilder.append(key, value)); diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 4242d843fb..5512711c61 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -52,7 +52,6 @@ import io.lettuce.core.tracing.Tracing; import io.netty.util.concurrent.EventExecutorGroup; import io.netty.util.concurrent.ImmediateEventExecutor; - /** * A reactive and thread-safe API for a Redis connection. * @@ -65,11 +64,12 @@ * @author Andrey Shlykov * @since 4.0 */ -public abstract class AbstractRedisReactiveCommands implements RedisHashReactiveCommands, - RedisKeyReactiveCommands, RedisStringReactiveCommands, RedisListReactiveCommands, - RedisSetReactiveCommands, RedisSortedSetReactiveCommands, RedisScriptingReactiveCommands, - RedisServerReactiveCommands, RedisHLLReactiveCommands, BaseRedisReactiveCommands, - RedisTransactionalReactiveCommands, RedisGeoReactiveCommands, RedisClusterReactiveCommands { +public abstract class AbstractRedisReactiveCommands implements RedisAclReactiveCommands, + RedisHashReactiveCommands, RedisKeyReactiveCommands, RedisStringReactiveCommands, + RedisListReactiveCommands, RedisSetReactiveCommands, RedisSortedSetReactiveCommands, + RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisHLLReactiveCommands, + BaseRedisReactiveCommands, RedisTransactionalReactiveCommands, RedisGeoReactiveCommands, + RedisClusterReactiveCommands { private final StatefulConnection connection; @@ -110,6 +110,81 @@ private EventExecutorGroup getScheduler() { return this.scheduler = schedulerToUse; } + @Override + public Flux aclCat() { + return createDissolvingFlux(commandBuilder::aclCat); + } + + @Override + public Flux aclCat(AclCategory category) { + return createDissolvingFlux(() -> commandBuilder.aclCat(category)); + } + + @Override + public Mono aclDeluser(String... usernames) { + return createMono(() -> commandBuilder.aclDeluser(usernames)); + } + + @Override + public Mono aclGenpass() { + return createMono(commandBuilder::aclGenpass); + } + + @Override + public Mono aclGenpass(int bits) { + return createMono(() -> commandBuilder.aclGenpass(bits)); + } + + @Override + public Mono> aclGetuser(String username) { + return createMono(() -> commandBuilder.aclGetuser(username)); + } + + @Override + public Flux aclList() { + return createDissolvingFlux(commandBuilder::aclList); + } + + @Override + public Mono aclLoad() { + return createMono(commandBuilder::aclLoad); + } + + @Override + public Flux> aclLog() { + return createDissolvingFlux(commandBuilder::aclLog); + } + + @Override + public Flux> aclLog(int count) { + return createDissolvingFlux(() -> commandBuilder.aclLog(count)); + } + + @Override + public Mono aclLogReset() { + return createMono(commandBuilder::aclLogReset); + } + + @Override + public Mono aclSave() { + return createMono(commandBuilder::aclSave); + } + + @Override + public Mono aclSetuser(String username, AclSetuserArgs args) { + return createMono(() -> commandBuilder.aclSetuser(username, args)); + } + + @Override + public Flux aclUsers() { + return createDissolvingFlux(commandBuilder::aclUsers); + } + + @Override + public Mono aclWhoami() { + return createMono(commandBuilder::aclWhoami); + } + @Override public Mono append(K key, V value) { return createMono(() -> commandBuilder.append(key, value)); diff --git a/src/main/java/io/lettuce/core/AclCategory.java b/src/main/java/io/lettuce/core/AclCategory.java new file mode 100644 index 0000000000..a7980ac2b0 --- /dev/null +++ b/src/main/java/io/lettuce/core/AclCategory.java @@ -0,0 +1,115 @@ +package io.lettuce.core; + +/** + * Enum object describing Redis ACL categories. + * + * @since 6.1 + * @author Mikhael Sokolov + */ +public enum AclCategory { + + /** + * command affects keyspace + */ + KEYSPACE, + + /** + * read command + */ + READ, + + /** + * write command + */ + WRITE, + + /** + * command for sets + */ + SET, + + /** + * command for sorted sets + */ + SORTEDSET, + + /** + * command for lists + */ + LIST, + + /** + * command for hash ops + */ + HASH, + + /** + * command for strings + */ + STRING, + + /** + * command for bitmaps + */ + BITMAP, + + /** + * command for hyperloglog + */ + HYPERLOGLOG, + + /** + * geo command + */ + GEO, + + /** + * streaming command + */ + STREAM, + + /** + * pubsub command + */ + PUBSUB, + + /** + * admin command + */ + ADMIN, + + /** + * fast command + */ + FAST, + + /** + * slow command + */ + SLOW, + + /** + * blocking command + */ + BLOCKING, + + /** + * dangerous command + */ + DANGEROUS, + + /** + * connection-establishing command + */ + CONNECTION, + + /** + * transactional command + */ + TRANSACTION, + + /** + * scripting command + */ + SCRIPTING +} \ No newline at end of file diff --git a/src/main/java/io/lettuce/core/AclSetuserArgs.java b/src/main/java/io/lettuce/core/AclSetuserArgs.java new file mode 100644 index 0000000000..5d38c27736 --- /dev/null +++ b/src/main/java/io/lettuce/core/AclSetuserArgs.java @@ -0,0 +1,713 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core; + +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandType; +import io.lettuce.core.protocol.ProtocolKeyword; + +import java.util.ArrayList; +import java.util.List; + +import static io.lettuce.core.protocol.CommandKeyword.*; + +/** + * Argument list builder for the Redis ACL SETUSER command. + *

+ * {@link AclSetuserArgs} is a mutable object and instances should be used only once to avoid shared mutable state. + * + * @author Mikhael Sokolov + * @since 6.2 + */ +public class AclSetuserArgs implements CompositeArgument { + + private boolean active; + + private List keyPatterns; + + private boolean allKeys; + + private boolean resetKeys; + + private List channelPatterns; + + private boolean allChannels; + + private boolean resetChannels; + + private List addCommands; + + private boolean allCommands; + + private List removeCommands; + + private boolean noCommands; + + private List addCategories; + + private List removeCategories; + + private boolean nopass; + + private List addPasswords; + + private List addHashedPasswords; + + private List removePasswords; + + private List removeHashedPasswords; + + private boolean reset; + + + /** + * Builder entry points for {@link AclSetuserArgs}. + */ + public static class Builder { + + /** + * Utility constructor. + */ + private Builder() { + } + + /** + * Creates new {@link AclSetuserArgs} and set user active. + * + * @return new {@link AclSetuserArgs} and set user active. + * @see AclSetuserArgs#on() + */ + public static AclSetuserArgs on() { + return new AclSetuserArgs().on(); + } + + /** + * Creates new {@link AclSetuserArgs} and set user inactive. + * + * @return new {@link AclSetuserArgs} and set user inactive. + * @see AclSetuserArgs#off() + */ + public static AclSetuserArgs off() { + return new AclSetuserArgs().off(); + } + + /** + * Creates new {@link AclSetuserArgs} and adds accessible key pattern. + * + * @param keyPattern accessible key pattern + * @return new {@link AclSetuserArgs} and adds accessible key pattern. + * @see AclSetuserArgs#keyPattern(String) + */ + public static AclSetuserArgs keyPattern(String keyPattern) { + return new AclSetuserArgs().keyPattern(keyPattern); + } + + /** + * Creates new {@link AclSetuserArgs} and allows the user to access all the keys. + * + * @return new {@link AclSetuserArgs} and allows the user to access all the keys. + * @see AclSetuserArgs#allKeys() + */ + public static AclSetuserArgs allKeys() { + return new AclSetuserArgs().allKeys(); + } + + /** + * Creates new {@link AclSetuserArgs} and removes all the key patterns from the list of key patterns the user can access. + * + * @return new {@link AclSetuserArgs} and removes all the key patterns from the list of key patterns the user can access. + * @see AclSetuserArgs#resetKeys() + */ + public static AclSetuserArgs resetKeys() { + return new AclSetuserArgs().resetKeys(); + } + + /** + * Creates new {@link AclSetuserArgs} and adds accessible channel pattern. + * + * @param channelPattern accessible channel pattern + * @return new {@link AclSetuserArgs} and adds accessible channel pattern. + * @see AclSetuserArgs#channelPattern(String) + */ + public static AclSetuserArgs channelPattern(String channelPattern) { + return new AclSetuserArgs().channelPattern(channelPattern); + } + + /** + * Creates new {@link AclSetuserArgs} and allows the user to access all the Pub/Sub channels. + * + * @return new {@link AclSetuserArgs} and allows the user to access all the Pub/Sub channels. + * @see AclSetuserArgs#allChannels() + */ + public static AclSetuserArgs allChannels() { + return new AclSetuserArgs().allChannels(); + } + + /** + * Creates new {@link AclSetuserArgs} and removes all channel patterns from the list of Pub/Sub channel patterns the user can access. + * + * @return new {@link AclSetuserArgs} and removes all channel patterns from the list of Pub/Sub channel patterns the user can access. + * @see AclSetuserArgs#resetChannels() + */ + public static AclSetuserArgs resetChannels() { + return new AclSetuserArgs().resetChannels(); + } + + /** + * Creates new {@link AclSetuserArgs} and adds this command to the list of the commands the user can call. + * + * @param command accessible command + * @return new {@link AclSetuserArgs} and adds this command to the list of the commands the user can call. + * @see AclSetuserArgs#addCommand(CommandType) + */ + public static AclSetuserArgs addCommand(CommandType command) { + return new AclSetuserArgs().addCommand(command); + } + + /** + * Creates new {@link AclSetuserArgs} and adds the specified command to the list of the commands the user can execute. + * + * @param command accessible command + * @param subCommand accessible subcommand + * @return new {@link AclSetuserArgs} and adds the specified command to the list of the commands the user can execute. + * @see AclSetuserArgs#addCommand(CommandType, ProtocolKeyword) + */ + public static AclSetuserArgs addCommand(CommandType command, ProtocolKeyword subCommand) { + return new AclSetuserArgs().addCommand(command, subCommand); + } + + /** + * Creates new {@link AclSetuserArgs} and adds all the commands there are in the server. + * + * @return new {@link AclSetuserArgs} and adds all the commands there are in the server. + * @see AclSetuserArgs#allCommands() + */ + public static AclSetuserArgs allCommands() { + return new AclSetuserArgs().allCommands(); + } + + /** + * Creates new {@link AclSetuserArgs} and removes this command to the list of the commands the user can call. + * + * @param command inaccessible command + * @return new {@link AclSetuserArgs} and removes this command to the list of the commands the user can call. + * @see AclSetuserArgs#removeCommand(CommandType) + */ + public static AclSetuserArgs removeCommand(CommandType command) { + return new AclSetuserArgs().removeCommand(command); + } + + /** + * Creates new {@link AclSetuserArgs} and removes the specified command to the list of the commands the user can execute. + * + * @param command inaccessible command + * @param subCommand inaccessible subcommand + * @return new {@link AclSetuserArgs} and removes the specified command to the list of the commands the user can execute. + * @see AclSetuserArgs#removeCommand(CommandType, ProtocolKeyword) + */ + public static AclSetuserArgs removeCommand(CommandType command, ProtocolKeyword subCommand) { + return new AclSetuserArgs().removeCommand(command, subCommand); + } + + /** + * Creates new {@link AclSetuserArgs} and removes all the commands the user can execute. + * + * @return new {@link AclSetuserArgs} and removes all the commands the user can execute. + * @see AclSetuserArgs#noCommands() + */ + public static AclSetuserArgs noCommands() { + return new AclSetuserArgs().noCommands(); + } + + /** + * Creates new {@link AclSetuserArgs} and adds all the commands in the specified category to the list of commands the user is able to execute. + * + * @param category specified category + * @return new {@link AclSetuserArgs} and adds all the commands in the specified category to the list of commands the user is able to execute. + * @see AclSetuserArgs#addCategory(AclCategory) + */ + public static AclSetuserArgs addCategory(AclCategory category) { + return new AclSetuserArgs().addCategory(category); + } + + /** + * Creates new {@link AclSetuserArgs} and removes all the commands in the specified category to the list of commands the user is able to execute. + * + * @param category specified category + * @return new {@link AclSetuserArgs} and removes all the commands in the specified category to the list of commands the user is able to execute. + * @see AclSetuserArgs#removeCategory(AclCategory) + */ + public static AclSetuserArgs removeCategory(AclCategory category) { + return new AclSetuserArgs().removeCategory(category); + } + + /** + * Creates new {@link AclSetuserArgs} and sets the user as a "no password". + * + * @return new {@link AclSetuserArgs} and sets the user as a "no password". + * @see AclSetuserArgs#nopass() + */ + public static AclSetuserArgs nopass() { + return new AclSetuserArgs().nopass(); + } + + /** + * Creates new {@link AclSetuserArgs} and adds the specified clear text password as an hashed password in the list of the users passwords. + * + * @param password clear text password + * @return new {@link AclSetuserArgs} and adds the specified clear text password as an hashed password in the list of the users passwords. + * @see AclSetuserArgs#addPassword(String) + */ + public static AclSetuserArgs addPassword(String password) { + return new AclSetuserArgs().addPassword(password); + } + + /** + * Creates new {@link AclSetuserArgs} and adds the specified hashed password to the list of user passwords. + * + * @param hashedPassword hashed password + * @return new {@link AclSetuserArgs} and adds the specified hashed password to the list of user passwords. + * @see AclSetuserArgs#addHashedPassword(String) + */ + public static AclSetuserArgs addHashedPassword(String hashedPassword) { + return new AclSetuserArgs().addHashedPassword(hashedPassword); + + } + + /** + * Creates new {@link AclSetuserArgs} and removes the specified clear text password as an hashed password in the list of the users passwords. + * + * @param password clear text password + * @return new {@link AclSetuserArgs} and removes the specified clear text password as an hashed password in the list of the users passwords. + * @see AclSetuserArgs#removePassword(String) + */ + public static AclSetuserArgs removePassword(String password) { + return new AclSetuserArgs().removePassword(password); + + } + + /** + * Creates new {@link AclSetuserArgs} and removes the specified hashed password to the list of user passwords. + * + * @param hashedPassword hashed password + * @return new {@link AclSetuserArgs} and removes the specified hashed password to the list of user passwords. + * @see AclSetuserArgs#removeHashedPassword(String) + */ + public static AclSetuserArgs removeHashedPassword(String hashedPassword) { + return new AclSetuserArgs().removeHashedPassword(hashedPassword); + + } + + /** + * Creates new {@link AclSetuserArgs} and removes any capability from the user. + * + * @return new {@link AclSetuserArgs} and removes any capability from the user. + * @see AclSetuserArgs#reset() + */ + public static AclSetuserArgs reset() { + return new AclSetuserArgs().reset(); + } + } + + /** + * Set user active. + * + * @return {@code this} + */ + public AclSetuserArgs on() { + this.active = true; + return this; + } + + /** + * Set user inactive. + * + * @return {@code this} + */ + public AclSetuserArgs off() { + this.active = false; + return this; + } + + /** + * Adds accessible key pattern. + * + * @param keyPattern accessible key pattern + * @return {@code this} + */ + public AclSetuserArgs keyPattern(String keyPattern) { + if (this.keyPatterns == null) { + this.keyPatterns = new ArrayList<>(); + } + this.keyPatterns.add(keyPattern); + return this; + } + + /** + * Allows the user to access all the keys. + * + * @return {@code this} + */ + public AclSetuserArgs allKeys() { + this.allKeys = true; + return this; + } + + /** + * Removes all the key patterns from the list of key patterns the user can access. + * + * @return {@code this} + */ + public AclSetuserArgs resetKeys() { + this.resetKeys = true; + return this; + } + + /** + * Adds accessible channel pattern. + * + * @param channelPattern accessible channel pattern + * @return {@code this} + */ + public AclSetuserArgs channelPattern(String channelPattern) { + if (this.channelPatterns == null) { + this.channelPatterns = new ArrayList<>(); + } + this.channelPatterns.add(channelPattern); + return this; + } + + /** + * Allows the user to access all the Pub/Sub channels. + * + * @return {@code this} + */ + public AclSetuserArgs allChannels() { + this.allChannels = true; + return this; + } + + /** + * Removes all channel patterns from the list of Pub/Sub channel patterns the user can access. + * + * @return {@code this} + */ + public AclSetuserArgs resetChannels() { + this.resetChannels = true; + return this; + } + + /** + * Adds this command to the list of the commands the user can call. + * + * @param command accessible command + * @return {@code this} + */ + public AclSetuserArgs addCommand(CommandType command) { + return addCommand(command, null); + } + + /** + * Adds all the commands there are in the server. + * + * @return {@code this} + */ + public AclSetuserArgs addCommand(CommandType command, ProtocolKeyword subCommand) { + if (this.addCommands == null) { + this.addCommands = new ArrayList<>(); + } + this.addCommands.add(new CommandSubcommandPair(command, subCommand)); + return this; + } + + /** + * Adds all the commands there are in the server. + * + * @return {@code this} + */ + public AclSetuserArgs allCommands() { + this.allCommands = true; + return this; + } + + /** + * Removes this command to the list of the commands the user can call. + * + * @param command inaccessible command + * @return {@code this} + */ + public AclSetuserArgs removeCommand(CommandType command) { + return removeCommand(command, null); + } + + /** + * Removes the specified command to the list of the commands the user can execute. + * + * @param command inaccessible command + * @param subCommand inaccessible subcommand + * @return {@code this} + */ + public AclSetuserArgs removeCommand(CommandType command, ProtocolKeyword subCommand) { + if (removeCommands == null) { + this.removeCommands = new ArrayList<>(); + } + this.removeCommands.add(new CommandSubcommandPair(command, subCommand)); + return this; + } + + /** + * Removes all the commands the user can execute. + * + * @return {@code this} + */ + public AclSetuserArgs noCommands() { + this.noCommands = false; + return this; + } + + /** + * Adds all the commands in the specified category to the list of commands the user is able to execute. + * + * @param category specified category + * @return {@code this} + */ + public AclSetuserArgs addCategory(AclCategory category) { + if (this.addCategories == null) { + this.addCategories = new ArrayList<>(); + } + this.addCategories.add(category); + return this; + } + + /** + * Removes all the commands in the specified category to the list of commands the user is able to execute. + * + * @param category specified category + * @return {@code this} + */ + public AclSetuserArgs removeCategory(AclCategory category) { + if (this.removeCategories == null) { + this.removeCategories = new ArrayList<>(); + } + this.removeCategories.add(category); + return this; + } + + /** + * Sets the user as a "no password". + * + * @return {@code this} + */ + public AclSetuserArgs nopass() { + this.nopass = true; + return this; + } + + /** + * Adds the specified clear text password as an hashed password in the list of the users passwords. + * + * @param password clear text password + * @return {@code this} + */ + public AclSetuserArgs addPassword(String password) { + if (this.addPasswords == null) { + this.addPasswords = new ArrayList<>(); + } + this.addPasswords.add(password); + return this; + } + + /** + * Adds the specified hashed password to the list of user passwords. + * + * @param hashedPassword hashed password + * @return {@code this} + */ + public AclSetuserArgs addHashedPassword(String hashedPassword) { + if (this.addHashedPasswords == null) { + this.addHashedPasswords = new ArrayList<>(); + } + this.addHashedPasswords.add(hashedPassword); + return this; + } + + /** + * Removes the specified clear text password as an hashed password in the list of the users passwords. + * + * @param password clear text password + * @return {@code this} + */ + public AclSetuserArgs removePassword(String password) { + if (this.removePasswords == null) { + this.removePasswords = new ArrayList<>(); + } + this.removePasswords.add(password); + return this; + } + + /** + * Removes the specified hashed password to the list of user passwords. + * + * @param hashedPassword hashed password + * @return {@code this} + */ + public AclSetuserArgs removeHashedPassword(String hashedPassword) { + if (this.removeHashedPasswords == null) { + this.removeHashedPasswords = new ArrayList<>(); + } + this.removeHashedPasswords.add(hashedPassword); + return this; + } + + /** + * Removes any capability from the user. + * + * @return {@code this} + */ + public AclSetuserArgs reset() { + this.reset = true; + return this; + } + + @Override + public void build(CommandArgs args) { + if (reset) { + args.add(RESET); + return; + } + + if (active) { + args.add(ON); + } else { + args.add(OFF); + } + + if (allKeys) { + args.add(ALLKEYS); + } + + if (resetKeys) { + args.add(RESETKEYS); + } + + if (keyPatterns != null) { + for (String glob : keyPatterns) { + args.add("~" + glob); + } + } + + if (allChannels) { + args.add(ALLCHANNELS); + } + + if (resetChannels) { + args.add(RESETCHANNELS); + } + + if (channelPatterns != null) { + for (String glob : channelPatterns) { + args.add("&" + glob); + } + } + + if (allCommands) { + args.add(ALLCOMMANDS); + } + + if (addCommands != null) { + for (CommandSubcommandPair command : addCommands) { + if (command.getSubCommand() == null) { + args.add("+" + command.getCommand().name()); + } else { + args.add("+" + command.getCommand().name() + "|" + command.getSubCommand().name()); + } + } + } + + if (noCommands) { + args.add(NOCOMMANDS); + } + + if (removeCommands != null) { + for (CommandSubcommandPair command : removeCommands) { + if (command.getSubCommand() == null) { + args.add("-" + command.getCommand().name()); + } else { + args.add("-" + command.getCommand().name() + "|" + command.getSubCommand().name()); + } + } + } + + if (removeCategories != null) { + for (AclCategory category : addCategories) { + args.add("+@" + category.name()); + } + } + + if (removeCategories != null) { + for (AclCategory category : removeCategories) { + args.add("-@" + category.name()); + } + } + + if (nopass) { + args.add(NOPASS); + } + + if (addPasswords != null) { + for (String password : addPasswords) { + args.add(">" + password); + } + } + + if (addHashedPasswords != null) { + for (String password : addHashedPasswords) { + args.add("#" + password); + } + } + + if (removePasswords != null) { + for (String password : removePasswords) { + args.add("<" + password); + } + } + + if (removeHashedPasswords != null) { + for (String password : removeHashedPasswords) { + args.add("!" + password); + } + } + } + + private static class CommandSubcommandPair { + + private final CommandType command; + private final ProtocolKeyword subCommand; + + private CommandSubcommandPair(CommandType command, ProtocolKeyword subCommand) { + this.command = command; + this.subCommand = subCommand; + } + + public CommandType getCommand() { + return command; + } + + public ProtocolKeyword getSubCommand() { + return subCommand; + } + } +} diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java index a1197c1f07..25c2cad6d2 100644 --- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java @@ -19,6 +19,7 @@ import static io.lettuce.core.protocol.CommandKeyword.*; import static io.lettuce.core.protocol.CommandType.*; import static io.lettuce.core.protocol.CommandType.COPY; +import static io.lettuce.core.protocol.CommandType.SAVE; import java.nio.ByteBuffer; import java.util.Arrays; @@ -35,11 +36,7 @@ import io.lettuce.core.models.stream.PendingMessage; import io.lettuce.core.models.stream.PendingMessages; import io.lettuce.core.output.*; -import io.lettuce.core.protocol.BaseRedisCommandBuilder; -import io.lettuce.core.protocol.Command; -import io.lettuce.core.protocol.CommandArgs; -import io.lettuce.core.protocol.CommandType; -import io.lettuce.core.protocol.RedisCommand; +import io.lettuce.core.protocol.*; /** * @param @@ -67,6 +64,104 @@ class RedisCommandBuilder extends BaseRedisCommandBuilder { super(codec); } + Command> aclCat() { + CommandArgs args = new CommandArgs<>(codec); + args.add(CAT); + return createCommand(ACL, new EnumListOutput<>(codec, AclCategory.class), args); + } + + Command> aclCat(AclCategory category) { + LettuceAssert.notNull(category, "Category " + MUST_NOT_BE_NULL); + CommandArgs args = new CommandArgs<>(codec); + args.add(CAT).add(category.name().toLowerCase()); + return createCommand(ACL, new EnumListOutput<>(codec, CommandType.class), args); + } + + Command aclDeluser(String... usernames) { + notEmpty(usernames); + CommandArgs args = new CommandArgs<>(codec); + args.add(DELUSER); + for (String username : usernames) { + args.add(username); + } + return createCommand(ACL, new IntegerOutput<>(codec), args); + } + + Command aclGenpass() { + CommandArgs args = new CommandArgs<>(codec); + args.add(GENPASS); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + + Command aclGenpass(int bits) { + CommandArgs args = new CommandArgs<>(codec); + args.add(GENPASS).add(bits); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + + Command> aclGetuser(String username) { + LettuceAssert.notNull(username, "Username " + MUST_NOT_BE_NULL); + CommandArgs args = new CommandArgs<>(codec); + args.add(GETUSER).add(username); + return createCommand(ACL, new UserRulesOutput<>(codec), args); + } + + Command> aclList() { + CommandArgs args = new CommandArgs<>(codec); + args.add(LIST); + return createCommand(ACL, new StringListOutput<>(codec), args); + } + + Command aclLoad() { + CommandArgs args = new CommandArgs<>(codec); + args.add(LOAD); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + + Command>> aclLog() { + CommandArgs args = new CommandArgs<>(codec); + args.add(LOG); + return new Command(ACL, new ListOfGenericMapsOutput<>(StringCodec.ASCII), args); + } + + Command>> aclLog(int count) { + CommandArgs args = new CommandArgs<>(codec); + args.add(LOG).add(count); + return new Command(ACL, new ListOfGenericMapsOutput<>(StringCodec.ASCII), args); + } + + Command aclLogReset() { + CommandArgs args = new CommandArgs<>(codec); + args.add(LOG).add(RESET); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + + Command aclSave() { + CommandArgs args = new CommandArgs<>(codec); + args.add(CommandKeyword.SAVE); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + + Command aclSetuser(String username, AclSetuserArgs setuserArgs) { + notNullKey(username); + CommandArgs args = new CommandArgs<>(codec); + args.add(SETUSER).add(username); + setuserArgs.build(args); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + + Command> aclUsers() { + CommandArgs args = new CommandArgs<>(codec); + args.add(USERS); + return createCommand(ACL, new StringListOutput<>(codec), args); + } + + Command aclWhoami() { + CommandArgs args = new CommandArgs<>(codec); + args.add(WHOAMI); + return createCommand(ACL, new StatusOutput<>(codec), args); + } + Command append(K key, V value) { notNullKey(key); diff --git a/src/main/java/io/lettuce/core/api/async/RedisAclAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisAclAsyncCommands.java new file mode 100644 index 0000000000..beef094657 --- /dev/null +++ b/src/main/java/io/lettuce/core/api/async/RedisAclAsyncCommands.java @@ -0,0 +1,149 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.api.async; + +import io.lettuce.core.AclCategory; +import io.lettuce.core.AclSetuserArgs; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.protocol.CommandType; + +import java.util.List; +import java.util.Map; + +/** + * Asynchronous executed commands for the ACL-API. + * + * @author Mark Paluch + * @author Mikhael Sokolov + * @since 6.1 + * @generated by io.lettuce.apigenerator.CreateAsyncApi + */ +public interface RedisAclAsyncCommands { + + /** + * The command shows the available ACL categories if called without arguments. + * + * @return List<AclCategory> a list of ACL categories or + */ + RedisFuture> aclCat(); + + /** + * The command shows all the Redis commands in the specified category. + * + * @param category the specified category + * @return List<CommandType> a list of commands inside a given category + */ + RedisFuture> aclCat(AclCategory category); + + /** + * Delete all the specified ACL users and terminate all the connections that are authenticated with such users. + * + * @param usernames the specified usernames + * @return Long The number of users that were deleted + */ + RedisFuture aclDeluser(String... usernames); + + /** + * The command generates a password. + * + * @return String bulk-string-reply 64 bytes string password representing 256 bits of pseudorandom data. + */ + RedisFuture aclGenpass(); + + /** + * The command generates a password. + * + * @param bits amount of bits + * @return String bulk-string-reply N/4 bytes string password representing N bits of pseudorandom data. + */ + RedisFuture aclGenpass(int bits); + + /** + * The command returns all the rules defined for an existing ACL user. + * + * @param username the specified username + * @return Map<String, Object> a map of ACL rule definitions for the user. + */ + RedisFuture> aclGetuser(String username); + + /** + * The command shows the currently active ACL rules in the Redis server. + * + * @return List<String> a list of strings. + */ + RedisFuture> aclList(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), this command + * will reload the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. + * + * @return String simple-string-reply OK or error message. + */ + RedisFuture aclLoad(); + + /** + * The command shows a list of recent ACL security events. + * + * @return List<Map<K,Object>> list of security events. + */ + RedisFuture>> aclLog(); + + /** + * The command shows a list of recent ACL security events. + * + * @param count max count of events + * @return List<Map<K, Object>> list of security events. + */ + RedisFuture>> aclLog(int count); + + /** + * The command clears ACL security events. + * + * @return String simple-string-reply OK if the security log was cleared. + */ + RedisFuture aclLogReset(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), + * this command will save the currently defined ACLs from the server memory to the ACL file. + * + * @return String simple-string-reply OK or error message. + */ + RedisFuture aclSave(); + + /** + * Create an ACL user with the specified rules or modify the rules of an existing user. + * + * @param username the specified username + * @param rules rules + * @return String simple-string-reply OK or error message. + */ + RedisFuture aclSetuser(String username, AclSetuserArgs setuserArgs); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return List<K> a list of usernames. + */ + RedisFuture> aclUsers(); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K bulk-string-reply the username of the current connection. + */ + RedisFuture aclWhoami(); +} diff --git a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java index 4cad414e0d..40c80b281f 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java @@ -27,7 +27,7 @@ * @author Mark Paluch * @since 3.0 */ -public interface RedisAsyncCommands extends BaseRedisAsyncCommands, RedisClusterAsyncCommands, +public interface RedisAsyncCommands extends BaseRedisAsyncCommands, RedisAclAsyncCommands, RedisClusterAsyncCommands, RedisGeoAsyncCommands, RedisHashAsyncCommands, RedisHLLAsyncCommands, RedisKeyAsyncCommands, RedisListAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisSetAsyncCommands, RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisAclReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisAclReactiveCommands.java new file mode 100644 index 0000000000..14b465afaa --- /dev/null +++ b/src/main/java/io/lettuce/core/api/reactive/RedisAclReactiveCommands.java @@ -0,0 +1,149 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.api.reactive; + +import io.lettuce.core.AclCategory; +import io.lettuce.core.AclSetuserArgs; +import io.lettuce.core.protocol.CommandType; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Map; + +/** + * Reactive executed commands for the ACL-API. + * + * @author Mark Paluch + * @author Mikhael Sokolov + * @since 6.1 + * @generated by io.lettuce.apigenerator.CreateReactiveApi + */ +public interface RedisAclReactiveCommands { + + /** + * The command shows the available ACL categories if called without arguments. + * + * @return AclCategory a list of ACL categories or + */ + Flux aclCat(); + + /** + * The command shows all the Redis commands in the specified category. + * + * @param category the specified category + * @return CommandType a list of commands inside a given category + */ + Flux aclCat(AclCategory category); + + /** + * Delete all the specified ACL users and terminate all the connections that are authenticated with such users. + * + * @param usernames the specified usernames + * @return Long The number of users that were deleted + */ + Mono aclDeluser(String... usernames); + + /** + * The command generates a password. + * + * @return String bulk-string-reply 64 bytes string password representing 256 bits of pseudorandom data. + */ + Mono aclGenpass(); + + /** + * The command generates a password. + * + * @param bits amount of bits + * @return String bulk-string-reply N/4 bytes string password representing N bits of pseudorandom data. + */ + Mono aclGenpass(int bits); + + /** + * The command returns all the rules defined for an existing ACL user. + * + * @param username the specified username + * @return Map<String, Object> a map of ACL rule definitions for the user. + */ + Mono> aclGetuser(String username); + + /** + * The command shows the currently active ACL rules in the Redis server. + * + * @return String a list of strings. + */ + Flux aclList(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), this command + * will reload the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. + * + * @return String simple-string-reply OK or error message. + */ + Mono aclLoad(); + + /** + * The command shows a list of recent ACL security events. + * + * @return Map<K,Object> list of security events. + */ + Flux> aclLog(); + + /** + * The command shows a list of recent ACL security events. + * + * @param count max count of events + * @return Map<K, Object> list of security events. + */ + Flux> aclLog(int count); + + /** + * The command clears ACL security events. + * + * @return String simple-string-reply OK if the security log was cleared. + */ + Mono aclLogReset(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), + * this command will save the currently defined ACLs from the server memory to the ACL file. + * + * @return String simple-string-reply OK or error message. + */ + Mono aclSave(); + + /** + * Create an ACL user with the specified rules or modify the rules of an existing user. + * + * @param username the specified username + * @param rules rules + * @return String simple-string-reply OK or error message. + */ + Mono aclSetuser(String username, AclSetuserArgs setuserArgs); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K a list of usernames. + */ + Flux aclUsers(); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K bulk-string-reply the username of the current connection. + */ + Mono aclWhoami(); +} diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java index be8c49ddfc..c754710c67 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java @@ -27,7 +27,7 @@ * @author Mark Paluch * @since 5.0 */ -public interface RedisReactiveCommands extends BaseRedisReactiveCommands, RedisClusterReactiveCommands, +public interface RedisReactiveCommands extends BaseRedisReactiveCommands, RedisAclReactiveCommands, RedisClusterReactiveCommands, RedisGeoReactiveCommands, RedisHashReactiveCommands, RedisHLLReactiveCommands, RedisKeyReactiveCommands, RedisListReactiveCommands, RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisSetReactiveCommands, RedisSortedSetReactiveCommands, diff --git a/src/main/java/io/lettuce/core/api/sync/RedisAclCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisAclCommands.java new file mode 100644 index 0000000000..401146c4bd --- /dev/null +++ b/src/main/java/io/lettuce/core/api/sync/RedisAclCommands.java @@ -0,0 +1,148 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.api.sync; + +import io.lettuce.core.AclCategory; +import io.lettuce.core.AclSetuserArgs; +import io.lettuce.core.protocol.CommandType; + +import java.util.List; +import java.util.Map; + +/** + * Synchronous executed commands for the ACL-API. + * + * @author Mark Paluch + * @author Mikhael Sokolov + * @since 6.1 + * @generated by io.lettuce.apigenerator.CreateSyncApi + */ +public interface RedisAclCommands { + + /** + * The command shows the available ACL categories if called without arguments. + * + * @return List<AclCategory> a list of ACL categories or + */ + List aclCat(); + + /** + * The command shows all the Redis commands in the specified category. + * + * @param category the specified category + * @return List<CommandType> a list of commands inside a given category + */ + List aclCat(AclCategory category); + + /** + * Delete all the specified ACL users and terminate all the connections that are authenticated with such users. + * + * @param usernames the specified usernames + * @return Long The number of users that were deleted + */ + Long aclDeluser(String... usernames); + + /** + * The command generates a password. + * + * @return String bulk-string-reply 64 bytes string password representing 256 bits of pseudorandom data. + */ + String aclGenpass(); + + /** + * The command generates a password. + * + * @param bits amount of bits + * @return String bulk-string-reply N/4 bytes string password representing N bits of pseudorandom data. + */ + String aclGenpass(int bits); + + /** + * The command returns all the rules defined for an existing ACL user. + * + * @param username the specified username + * @return Map<String, Object> a map of ACL rule definitions for the user. + */ + Map aclGetuser(String username); + + /** + * The command shows the currently active ACL rules in the Redis server. + * + * @return List<String> a list of strings. + */ + List aclList(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), this command + * will reload the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. + * + * @return String simple-string-reply OK or error message. + */ + String aclLoad(); + + /** + * The command shows a list of recent ACL security events. + * + * @return List<Map<K,Object>> list of security events. + */ + List> aclLog(); + + /** + * The command shows a list of recent ACL security events. + * + * @param count max count of events + * @return List<Map<K, Object>> list of security events. + */ + List> aclLog(int count); + + /** + * The command clears ACL security events. + * + * @return String simple-string-reply OK if the security log was cleared. + */ + String aclLogReset(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), + * this command will save the currently defined ACLs from the server memory to the ACL file. + * + * @return String simple-string-reply OK or error message. + */ + String aclSave(); + + /** + * Create an ACL user with the specified rules or modify the rules of an existing user. + * + * @param username the specified username + * @param rules rules + * @return String simple-string-reply OK or error message. + */ + String aclSetuser(String username, AclSetuserArgs setuserArgs); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return List<K> a list of usernames. + */ + List aclUsers(); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K bulk-string-reply the username of the current connection. + */ + String aclWhoami(); +} diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCommands.java index 875e076105..69371de1e9 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCommands.java @@ -27,7 +27,7 @@ * @author Mark Paluch * @since 3.0 */ -public interface RedisCommands extends BaseRedisCommands, RedisClusterCommands, RedisGeoCommands, +public interface RedisCommands extends BaseRedisCommands, RedisAclCommands, RedisClusterCommands, RedisGeoCommands, RedisHashCommands, RedisHLLCommands, RedisKeyCommands, RedisListCommands, RedisScriptingCommands, RedisServerCommands, RedisSetCommands, RedisSortedSetCommands, RedisStreamCommands, RedisStringCommands, RedisTransactionalCommands { 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 new file mode 100644 index 0000000000..9293fce297 --- /dev/null +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAclAsyncCommands.java @@ -0,0 +1,148 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.cluster.api.async; + +import io.lettuce.core.AclCategory; +import io.lettuce.core.AclSetuserArgs; +import io.lettuce.core.protocol.CommandType; + +import java.util.List; +import java.util.Map; + +/** + * Asynchronous executed commands on a node selection for the ACL-API. + * + * @author Mark Paluch + * @author Mikhael Sokolov + * @since 6.1 + * @generated by io.lettuce.apigenerator.CreateAsyncNodeSelectionClusterApi + */ +public interface NodeSelectionAclAsyncCommands { + + /** + * The command shows the available ACL categories if called without arguments. + * + * @return List<AclCategory> a list of ACL categories or + */ + AsyncExecutions> aclCat(); + + /** + * The command shows all the Redis commands in the specified category. + * + * @param category the specified category + * @return List<CommandType> a list of commands inside a given category + */ + AsyncExecutions> aclCat(AclCategory category); + + /** + * Delete all the specified ACL users and terminate all the connections that are authenticated with such users. + * + * @param usernames the specified usernames + * @return Long The number of users that were deleted + */ + AsyncExecutions aclDeluser(String... usernames); + + /** + * The command generates a password. + * + * @return String bulk-string-reply 64 bytes string password representing 256 bits of pseudorandom data. + */ + AsyncExecutions aclGenpass(); + + /** + * The command generates a password. + * + * @param bits amount of bits + * @return String bulk-string-reply N/4 bytes string password representing N bits of pseudorandom data. + */ + AsyncExecutions aclGenpass(int bits); + + /** + * The command returns all the rules defined for an existing ACL user. + * + * @param username the specified username + * @return Map<String, Object> a map of ACL rule definitions for the user. + */ + AsyncExecutions> aclGetuser(String username); + + /** + * The command shows the currently active ACL rules in the Redis server. + * + * @return List<String> a list of strings. + */ + AsyncExecutions> aclList(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), this command + * will reload the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. + * + * @return String simple-string-reply OK or error message. + */ + AsyncExecutions aclLoad(); + + /** + * The command shows a list of recent ACL security events. + * + * @return List<Map<K,Object>> list of security events. + */ + AsyncExecutions>> aclLog(); + + /** + * The command shows a list of recent ACL security events. + * + * @param count max count of events + * @return List<Map<K, Object>> list of security events. + */ + AsyncExecutions>> aclLog(int count); + + /** + * The command clears ACL security events. + * + * @return String simple-string-reply OK if the security log was cleared. + */ + AsyncExecutions aclLogReset(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), + * this command will save the currently defined ACLs from the server memory to the ACL file. + * + * @return String simple-string-reply OK or error message. + */ + AsyncExecutions aclSave(); + + /** + * Create an ACL user with the specified rules or modify the rules of an existing user. + * + * @param username the specified username + * @param rules rules + * @return String simple-string-reply OK or error message. + */ + AsyncExecutions aclSetuser(String username, AclSetuserArgs setuserArgs); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return List<K> a list of usernames. + */ + AsyncExecutions> aclUsers(); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K bulk-string-reply the username of the current connection. + */ + AsyncExecutions aclWhoami(); +} diff --git a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java index 412e3d72fe..0ba2030af0 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java @@ -18,7 +18,6 @@ import java.time.Duration; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import io.lettuce.core.KeyValue; import io.lettuce.core.RedisFuture; @@ -32,7 +31,7 @@ * @author Mark Paluch * @since 4.0 */ -public interface RedisClusterAsyncCommands extends BaseRedisAsyncCommands, RedisGeoAsyncCommands, +public interface RedisClusterAsyncCommands extends BaseRedisAsyncCommands, RedisAclAsyncCommands, RedisGeoAsyncCommands, RedisHashAsyncCommands, RedisHLLAsyncCommands, RedisKeyAsyncCommands, RedisListAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisSetAsyncCommands, RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, RedisStringAsyncCommands { diff --git a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java index 4e79af4504..1fcb0456bb 100644 --- a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java @@ -17,7 +17,6 @@ import java.time.Duration; import java.util.Map; -import java.util.concurrent.TimeUnit; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -33,10 +32,11 @@ * @since 5.0 */ public interface RedisClusterReactiveCommands - extends BaseRedisReactiveCommands, RedisGeoReactiveCommands, RedisHashReactiveCommands, - RedisHLLReactiveCommands, RedisKeyReactiveCommands, RedisListReactiveCommands, - RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisSetReactiveCommands, - RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, RedisStringReactiveCommands { + extends BaseRedisReactiveCommands, RedisAclReactiveCommands, RedisGeoReactiveCommands, + RedisHashReactiveCommands, RedisHLLReactiveCommands, RedisKeyReactiveCommands, + RedisListReactiveCommands, RedisScriptingReactiveCommands, RedisServerReactiveCommands, + RedisSetReactiveCommands, RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, + RedisStringReactiveCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. 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 new file mode 100644 index 0000000000..e6b2535391 --- /dev/null +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionAclCommands.java @@ -0,0 +1,148 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.cluster.api.sync; + +import io.lettuce.core.AclCategory; +import io.lettuce.core.AclSetuserArgs; +import io.lettuce.core.protocol.CommandType; + +import java.util.List; +import java.util.Map; + +/** + * Synchronous executed commands on a node selection for the ACL-API. + * + * @author Mark Paluch + * @author Mikhael Sokolov + * @since 6.1 + * @generated by io.lettuce.apigenerator.CreateSyncNodeSelectionClusterApi + */ +public interface NodeSelectionAclCommands { + + /** + * The command shows the available ACL categories if called without arguments. + * + * @return List<AclCategory> a list of ACL categories or + */ + Executions> aclCat(); + + /** + * The command shows all the Redis commands in the specified category. + * + * @param category the specified category + * @return List<CommandType> a list of commands inside a given category + */ + Executions> aclCat(AclCategory category); + + /** + * Delete all the specified ACL users and terminate all the connections that are authenticated with such users. + * + * @param usernames the specified usernames + * @return Long The number of users that were deleted + */ + Executions aclDeluser(String... usernames); + + /** + * The command generates a password. + * + * @return String bulk-string-reply 64 bytes string password representing 256 bits of pseudorandom data. + */ + Executions aclGenpass(); + + /** + * The command generates a password. + * + * @param bits amount of bits + * @return String bulk-string-reply N/4 bytes string password representing N bits of pseudorandom data. + */ + Executions aclGenpass(int bits); + + /** + * The command returns all the rules defined for an existing ACL user. + * + * @param username the specified username + * @return Map<String, Object> a map of ACL rule definitions for the user. + */ + Executions> aclGetuser(String username); + + /** + * The command shows the currently active ACL rules in the Redis server. + * + * @return List<String> a list of strings. + */ + Executions> aclList(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), this command + * will reload the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. + * + * @return String simple-string-reply OK or error message. + */ + Executions aclLoad(); + + /** + * The command shows a list of recent ACL security events. + * + * @return List<Map<K,Object>> list of security events. + */ + Executions>> aclLog(); + + /** + * The command shows a list of recent ACL security events. + * + * @param count max count of events + * @return List<Map<K, Object>> list of security events. + */ + Executions>> aclLog(int count); + + /** + * The command clears ACL security events. + * + * @return String simple-string-reply OK if the security log was cleared. + */ + Executions aclLogReset(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), + * this command will save the currently defined ACLs from the server memory to the ACL file. + * + * @return String simple-string-reply OK or error message. + */ + Executions aclSave(); + + /** + * Create an ACL user with the specified rules or modify the rules of an existing user. + * + * @param username the specified username + * @param rules rules + * @return String simple-string-reply OK or error message. + */ + Executions aclSetuser(String username, AclSetuserArgs setuserArgs); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return List<K> a list of usernames. + */ + Executions> aclUsers(); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K bulk-string-reply the username of the current connection. + */ + Executions aclWhoami(); +} diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java index 146e44d622..423a5c56da 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java @@ -17,7 +17,6 @@ import java.time.Duration; import java.util.List; -import java.util.concurrent.TimeUnit; import io.lettuce.core.api.sync.*; @@ -30,9 +29,11 @@ * @since 4.0 */ public interface RedisClusterCommands - extends BaseRedisCommands, RedisGeoCommands, RedisHashCommands, RedisHLLCommands, - RedisKeyCommands, RedisListCommands, RedisScriptingCommands, RedisServerCommands, - RedisSetCommands, RedisSortedSetCommands, RedisStreamCommands, RedisStringCommands { + extends BaseRedisCommands, RedisAclCommands, RedisGeoCommands, + RedisHashCommands, RedisHLLCommands, RedisKeyCommands, + RedisListCommands, RedisScriptingCommands, RedisServerCommands, + RedisSetCommands, RedisSortedSetCommands, RedisStreamCommands, + RedisStringCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. diff --git a/src/main/java/io/lettuce/core/models/command/CommandDetail.java b/src/main/java/io/lettuce/core/models/command/CommandDetail.java index 50951f8834..6bd9f29320 100644 --- a/src/main/java/io/lettuce/core/models/command/CommandDetail.java +++ b/src/main/java/io/lettuce/core/models/command/CommandDetail.java @@ -15,6 +15,8 @@ */ package io.lettuce.core.models.command; +import io.lettuce.core.AclCategory; + import java.io.Serializable; import java.util.Set; @@ -226,115 +228,4 @@ public enum Flag { */ MOVABLEKEYS; } - - /** - * @since 6.1 - */ - public enum AclCategory { - - /** - * command affects keyspace - */ - KEYSPACE, - - /** - * read command - */ - READ, - - /** - * write command - */ - WRITE, - - /** - * command for sets - */ - SET, - - /** - * command for sorted sets - */ - SORTEDSET, - - /** - * command for lists - */ - LIST, - - /** - * command for hash ops - */ - HASH, - - /** - * command for strings - */ - STRING, - - /** - * command for bitmaps - */ - BITMAP, - - /** - * command for hyperloglog - */ - HYPERLOGLOG, - - /** - * geo command - */ - GEO, - - /** - * streaming command - */ - STREAM, - - /** - * pubsub command - */ - PUBSUB, - - /** - * admin command - */ - ADMIN, - - /** - * fast command - */ - FAST, - - /** - * slow command - */ - SLOW, - - /** - * blocking command - */ - BLOCKING, - - /** - * dangerous command - */ - DANGEROUS, - - /** - * connection-establishing command - */ - CONNECTION, - - /** - * transactional command - */ - TRANSACTION, - - /** - * scripting command - */ - SCRIPTING - } } diff --git a/src/main/java/io/lettuce/core/models/command/CommandDetailParser.java b/src/main/java/io/lettuce/core/models/command/CommandDetailParser.java index 608d6adecc..71de0ac274 100644 --- a/src/main/java/io/lettuce/core/models/command/CommandDetailParser.java +++ b/src/main/java/io/lettuce/core/models/command/CommandDetailParser.java @@ -17,6 +17,7 @@ import java.util.*; +import io.lettuce.core.AclCategory; import io.lettuce.core.internal.LettuceAssert; /** @@ -40,7 +41,7 @@ public class CommandDetailParser { protected static final Map FLAG_MAPPING; @SuppressWarnings("serial") - protected static final Map ACL_CATEGORY_MAPPING; + protected static final Map ACL_CATEGORY_MAPPING; static { Map flagMap = new HashMap<>(); @@ -60,28 +61,28 @@ public class CommandDetailParser { flagMap.put("write", CommandDetail.Flag.WRITE); FLAG_MAPPING = Collections.unmodifiableMap(flagMap); - Map aclCategoriesMap = new HashMap<>(); - aclCategoriesMap.put("@keyspace", CommandDetail.AclCategory.KEYSPACE); - aclCategoriesMap.put("@read", CommandDetail.AclCategory.READ); - aclCategoriesMap.put("@write", CommandDetail.AclCategory.WRITE); - aclCategoriesMap.put("@set", CommandDetail.AclCategory.SET); - aclCategoriesMap.put("@sortedset", CommandDetail.AclCategory.SORTEDSET); - aclCategoriesMap.put("@list", CommandDetail.AclCategory.LIST); - aclCategoriesMap.put("@hash", CommandDetail.AclCategory.HASH); - aclCategoriesMap.put("@string", CommandDetail.AclCategory.STRING); - aclCategoriesMap.put("@bitmap", CommandDetail.AclCategory.BITMAP); - aclCategoriesMap.put("@hyperloglog", CommandDetail.AclCategory.HYPERLOGLOG); - aclCategoriesMap.put("@geo", CommandDetail.AclCategory.GEO); - aclCategoriesMap.put("@stream", CommandDetail.AclCategory.STREAM); - aclCategoriesMap.put("@pubsub", CommandDetail.AclCategory.PUBSUB); - aclCategoriesMap.put("@admin", CommandDetail.AclCategory.ADMIN); - aclCategoriesMap.put("@fast", CommandDetail.AclCategory.FAST); - aclCategoriesMap.put("@slow", CommandDetail.AclCategory.SLOW); - aclCategoriesMap.put("@blocking", CommandDetail.AclCategory.BLOCKING); - aclCategoriesMap.put("@dangerous", CommandDetail.AclCategory.DANGEROUS); - aclCategoriesMap.put("@connection", CommandDetail.AclCategory.CONNECTION); - aclCategoriesMap.put("@transaction", CommandDetail.AclCategory.TRANSACTION); - aclCategoriesMap.put("@scripting", CommandDetail.AclCategory.SCRIPTING); + Map aclCategoriesMap = new HashMap<>(); + aclCategoriesMap.put("@keyspace", AclCategory.KEYSPACE); + aclCategoriesMap.put("@read", AclCategory.READ); + aclCategoriesMap.put("@write", AclCategory.WRITE); + aclCategoriesMap.put("@set", AclCategory.SET); + aclCategoriesMap.put("@sortedset", AclCategory.SORTEDSET); + aclCategoriesMap.put("@list", AclCategory.LIST); + aclCategoriesMap.put("@hash", AclCategory.HASH); + aclCategoriesMap.put("@string", AclCategory.STRING); + aclCategoriesMap.put("@bitmap", AclCategory.BITMAP); + aclCategoriesMap.put("@hyperloglog", AclCategory.HYPERLOGLOG); + aclCategoriesMap.put("@geo", AclCategory.GEO); + aclCategoriesMap.put("@stream", AclCategory.STREAM); + aclCategoriesMap.put("@pubsub", AclCategory.PUBSUB); + aclCategoriesMap.put("@admin", AclCategory.ADMIN); + aclCategoriesMap.put("@fast", AclCategory.FAST); + aclCategoriesMap.put("@slow", AclCategory.SLOW); + aclCategoriesMap.put("@blocking", AclCategory.BLOCKING); + aclCategoriesMap.put("@dangerous", AclCategory.DANGEROUS); + aclCategoriesMap.put("@connection", AclCategory.CONNECTION); + aclCategoriesMap.put("@transaction", AclCategory.TRANSACTION); + aclCategoriesMap.put("@scripting", AclCategory.SCRIPTING); ACL_CATEGORY_MAPPING = Collections.unmodifiableMap(aclCategoriesMap); } @@ -127,7 +128,7 @@ private static CommandDetail parseCommandDetail(Collection collection) { Object categories = iterator.hasNext() ? iterator.next() : null; Set parsedFlags = parseFlags(flags); - Set parsedAclCategories = parseAclCategories(categories); + Set parsedAclCategories = parseAclCategories(categories); return new CommandDetail(name, arity, parsedFlags, firstKey, lastKey, keyStepCount, parsedAclCategories); } @@ -148,13 +149,13 @@ private static Set parseFlags(Object flags) { return Collections.unmodifiableSet(result); } - private static Set parseAclCategories(Object aclCategories) { - Set result = new HashSet<>(); + private static Set parseAclCategories(Object aclCategories) { + Set result = new HashSet<>(); if (aclCategories instanceof Collection) { Collection collection = (Collection) aclCategories; for (Object o : collection) { - CommandDetail.AclCategory aclCategory = ACL_CATEGORY_MAPPING.get(o); + AclCategory aclCategory = ACL_CATEGORY_MAPPING.get(o); if (aclCategory != null) { result.add(aclCategory); } diff --git a/src/main/java/io/lettuce/core/output/EnumListOutput.java b/src/main/java/io/lettuce/core/output/EnumListOutput.java new file mode 100644 index 0000000000..a899fd68f1 --- /dev/null +++ b/src/main/java/io/lettuce/core/output/EnumListOutput.java @@ -0,0 +1,63 @@ +package io.lettuce.core.output; + +import io.lettuce.core.codec.RedisCodec; +import io.lettuce.core.internal.LettuceAssert; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; + +/** + * {@link List} of enums output. + * + * @param Key type. + * @param Value type. + * @author Mikhael Sokolov + */ +public class EnumListOutput> extends CommandOutput> implements StreamingOutput { + + private boolean initialized; + + private Subscriber subscriber; + + private final Class enumClass; + + public EnumListOutput(RedisCodec codec, Class enumClass) { + super(codec, Collections.emptyList()); + setSubscriber(ListSubscriber.instance()); + this.enumClass = enumClass; + } + + @Override + public void set(ByteBuffer bytes) { + subscriber.onNext(output, bytes == null ? null : valueOfOrNull(decodeAscii(bytes))); + } + + @Override + public void multi(int count) { + + if (!initialized) { + output = OutputFactory.newList(count); + initialized = true; + } + } + + @Override + public void setSubscriber(Subscriber subscriber) { + LettuceAssert.notNull(subscriber, "Subscriber must not be null"); + this.subscriber = subscriber; + } + + @Override + public Subscriber getSubscriber() { + return subscriber; + } + + private E valueOfOrNull(String value) { + try { + return Enum.valueOf(enumClass, value.toLowerCase()); + } catch (IllegalArgumentException e) { + return null; + } + } +} diff --git a/src/main/java/io/lettuce/core/output/ListOfGenericMapsOutput.java b/src/main/java/io/lettuce/core/output/ListOfGenericMapsOutput.java new file mode 100644 index 0000000000..8c461ef9c9 --- /dev/null +++ b/src/main/java/io/lettuce/core/output/ListOfGenericMapsOutput.java @@ -0,0 +1,87 @@ +/* + * Copyright 2011-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.output; + +import io.lettuce.core.codec.RedisCodec; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * {@link List} of maps output. + * + * @param Key type. + * @param Value type. + * + * @author Will Glozer + */ +public class ListOfGenericMapsOutput extends CommandOutput>> { + + private GenericMapOutput nested; + + private int mapCount = -1; + + private final List counts = new ArrayList<>(); + + public ListOfGenericMapsOutput(RedisCodec codec) { + super(codec, new ArrayList<>()); + nested = new GenericMapOutput<>(codec); + } + + @Override + public void set(ByteBuffer bytes) { + nested.set(bytes); + } + + @Override + public void complete(int depth) { + if (counts.size() > 0) { + int expectedSize = counts.get(0); + + if (nested.get().size() == expectedSize) { + counts.remove(0); + output.add(new LinkedHashMap<>(nested.get())); + nested.get().clear(); + } + } + } + + @Override + public void multi(int count) { + + nested.multi(count); + + if (mapCount == -1) { + mapCount = count; + } else { + // div 2 because of key value pair counts twice + counts.add(count / 2); + } + } + + @Override + public void set(long integer) { + nested.set(integer); + } + + @Override + public void set(double number) { + nested.set(number); + } +} \ No newline at end of file diff --git a/src/main/java/io/lettuce/core/output/ListOfMapsOutput.java b/src/main/java/io/lettuce/core/output/ListOfMapsOutput.java index 39fefe6851..a7d8d1502f 100644 --- a/src/main/java/io/lettuce/core/output/ListOfMapsOutput.java +++ b/src/main/java/io/lettuce/core/output/ListOfMapsOutput.java @@ -76,4 +76,8 @@ public void multi(int count) { } } + @Override + public void set(long integer) { + nested.set(integer); + } } diff --git a/src/main/java/io/lettuce/core/output/UserRulesOutput.java b/src/main/java/io/lettuce/core/output/UserRulesOutput.java new file mode 100644 index 0000000000..49e0fc2808 --- /dev/null +++ b/src/main/java/io/lettuce/core/output/UserRulesOutput.java @@ -0,0 +1,90 @@ +/* + * Copyright 2019-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.output; + +import io.lettuce.core.codec.RedisCodec; + +import java.nio.ByteBuffer; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * {@link Map} of usernames and rules output. + * + * @param Key type. + * @param Value type. + * + * @author Mikhael Sokolov + */ +public class UserRulesOutput extends CommandOutput> { + + private String username; + + public UserRulesOutput(RedisCodec codec) { + super(codec, null); + } + + @Override + public void set(ByteBuffer bytes) { + if (username == null) { + username = (bytes == null) ? null : decodeAscii(bytes); + return; + } + + Object value = (bytes == null) ? null : codec.decodeValue(bytes); + output.put(username, value); + username = null; + } + + @Override + public void setBigNumber(ByteBuffer bytes) { + set(bytes); + } + + @Override + @SuppressWarnings("unchecked") + public void set(long integer) { + + if (username == null) { + username = String.valueOf(integer); + return; + } + + V value = (V) Long.valueOf(integer); + output.put(username, value); + username = null; + } + + @Override + public void set(double number) { + + if (username == null) { + username = String.valueOf(number); + return; + } + + Object value = number; + output.put(username, value); + username = null; + } + + @Override + public void multi(int count) { + if (output == null) { + output = new LinkedHashMap<>(count / 2, 1); + } + } +} diff --git a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java index b52566bd85..7c0fc9ff3e 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java +++ b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java @@ -27,21 +27,21 @@ */ public enum CommandKeyword implements ProtocolKeyword { - ADDR, ADDSLOTS, AFTER, AGGREGATE, ALPHA, AND, ASK, ASC, ASYNC, BEFORE, BLOCK, BUMPEPOCH, + ADDR, ADDSLOTS, AFTER, AGGREGATE, ALLCHANNELS, ALLCOMMANDS, ALLKEYS, ALPHA, AND, ASK, ASC, ASYNC, BEFORE, BLOCK, BUMPEPOCH, - BY, BYLEX, BYSCORE, CACHING, CHANNELS, COPY, COUNT, COUNTKEYSINSLOT, CONSUMERS, CREATE, DB, DELSLOTS, DESC, SOFT, HARD, ENCODING, + BY, BYLEX, BYSCORE, CACHING, CAT, CHANNELS, COPY, COUNT, COUNTKEYSINSLOT, CONSUMERS, CREATE, DB, DELSLOTS, DELUSER, DESC, SOFT, HARD, ENCODING, - FAILOVER, FORGET, FLUSH, FORCE, FLUSHSLOTS, GETNAME, GETKEYSINSLOT, GETREDIR, GROUP, GROUPS, HTSTATS, ID, IDLE, + FAILOVER, FORGET, FLUSH, FORCE, FLUSHSLOTS, GENPASS, GETNAME, GETUSER, GETKEYSINSLOT, GETREDIR, GROUP, GROUPS, HTSTATS, ID, IDLE, - IDLETIME, JUSTID, KILL, KEYSLOT, LEFT, LEN, LIMIT, LIST, LOAD, MATCH, + IDLETIME, JUSTID, KILL, KEYSLOT, LEFT, LEN, LIMIT, LIST, LOAD, LOG, MATCH, - MAX, MAXLEN, MEET, MIN, MOVED, NO, NOACK, NODE, NODES, NOMKSTREAM, NOSAVE, NOT, NUMSUB, NUMPAT, OFF, ON, ONE, OR, PAUSE, + MAX, MAXLEN, MEET, MIN, MOVED, NO, NOACK, NOCOMMANDS, NODE, NODES, NOMKSTREAM, NOPASS, NOSAVE, NOT, NUMSUB, NUMPAT, OFF, ON, ONE, OR, PAUSE, - REFCOUNT, REMOVE, RELOAD, REPLACE, REPLICATE, REV, RESET, + REFCOUNT, REMOVE, RELOAD, REPLACE, REPLICATE, REV, RESET, RESETCHANNELS, RESETKEYS, RESETSTAT, RESTART, RETRYCOUNT, REWRITE, RIGHT, SAVECONFIG, SDSLEN, SETNAME, SETSLOT, SLOTS, STABLE, - MIGRATING, IMPORTING, SKIPME, SLAVES, STREAM, STORE, SUM, SEGFAULT, TRACKING, TYPE, UNBLOCK, WEIGHTS, + MIGRATING, IMPORTING, SAVE, SKIPME, SLAVES, STREAM, STORE, SUM, SEGFAULT, SETUSER, TRACKING, TYPE, UNBLOCK, USERS, WEIGHTS, WHOAMI, WITHSCORES, XOR, YES, USAGE; diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommands.kt new file mode 100644 index 0000000000..6c9533aeb2 --- /dev/null +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommands.kt @@ -0,0 +1,150 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.lettuce.core.api.coroutines + +import io.lettuce.core.ExperimentalLettuceCoroutinesApi +import kotlinx.coroutines.flow.Flow +import io.lettuce.core.* +import io.lettuce.core.protocol.CommandType + +/** + * Coroutine executed commands for the ACL-API. + * + * @author Mikhael Sokolov + * @author Mikhael Sokolov + * @since 6.1 + * @generated by io.lettuce.apigenerator.CreateKotlinCoroutinesApi + */ +@ExperimentalLettuceCoroutinesApi +interface RedisAclCoroutinesCommands { + + /** + * The command shows the available ACL categories if called without arguments. + * + * @return List a list of ACL categories or + */ + suspend fun aclCat(): List + + /** + * The command shows all the Redis commands in the specified category. + * + * @param category the specified category + * @return List a list of commands inside a given category + */ + suspend fun aclCat(category: AclCategory): List + + /** + * Delete all the specified ACL users and terminate all the connections that are authenticated with such users. + * + * @param usernames the specified usernames + * @return Long The number of users that were deleted + */ + suspend fun aclDeluser(vararg usernames: String): Long? + + /** + * The command generates a password. + * + * @return String bulk-string-reply 64 bytes string password representing 256 bits of pseudorandom data. + */ + suspend fun aclGenpass(): String? + + /** + * The command generates a password. + * + * @param bits amount of bits + * @return String bulk-string-reply N/4 bytes string password representing N bits of pseudorandom data. + */ + suspend fun aclGenpass(bits: Int): String? + + /** + * The command returns all the rules defined for an existing ACL user. + * + * @param username the specified username + * @return Map a map of ACL rule definitions for the user. + */ + suspend fun aclGetuser(username: String): Map? + + /** + * The command shows the currently active ACL rules in the Redis server. + * + * @return List a list of strings. + */ + fun aclList(): Flow + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), this command + * will reload the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. + * + * @return String simple-string-reply OK or error message. + */ + suspend fun aclLoad(): String? + + /** + * The command shows a list of recent ACL security events. + * + * @return List> list of security events. + */ + fun aclLog(): Flow> + + /** + * The command shows a list of recent ACL security events. + * + * @param count max count of events + * @return List> list of security events. + */ + fun aclLog(count: Int): Flow> + + /** + * The command clears ACL security events. + * + * @return String simple-string-reply OK if the security log was cleared. + */ + suspend fun aclLogReset(): String? + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), + * this command will save the currently defined ACLs from the server memory to the ACL file. + * + * @return String simple-string-reply OK or error message. + */ + suspend fun aclSave(): String? + + /** + * Create an ACL user with the specified rules or modify the rules of an existing user. + * + * @param username the specified username + * @param rules rules + * @return String simple-string-reply OK or error message. + */ + suspend fun aclSetuser(username: String, setuserArgs: AclSetuserArgs): String? + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return List a list of usernames. + */ + suspend fun aclUsers(): List + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K bulk-string-reply the username of the current connection. + */ + suspend fun aclWhoami(): String? + +} + diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommandsImpl.kt new file mode 100644 index 0000000000..6fb95323e2 --- /dev/null +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisAclCoroutinesCommandsImpl.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2020-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.lettuce.core.api.coroutines + +import io.lettuce.core.AclCategory +import io.lettuce.core.AclSetuserArgs +import io.lettuce.core.ExperimentalLettuceCoroutinesApi +import io.lettuce.core.api.reactive.RedisAclReactiveCommands +import io.lettuce.core.protocol.CommandType +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.asFlow +import kotlinx.coroutines.reactive.awaitFirstOrNull + + +/** + * Coroutine executed commands (based on reactive commands) for basic commands. + * + * @param Key type. + * @param Value type. + * @author Mikhael Sokolov + * @since 6.0 + */ +@ExperimentalLettuceCoroutinesApi +internal class RedisAclCoroutinesCommandsImpl(internal val ops: RedisAclReactiveCommands) : RedisAclCoroutinesCommands { + + override suspend fun aclCat(): List = ops.aclCat().asFlow().toList() + + override suspend fun aclCat(category: AclCategory): List = ops.aclCat(category).asFlow().toList() + + override suspend fun aclDeluser(vararg usernames: String): Long? = ops.aclDeluser(*usernames).awaitFirstOrNull() + + override suspend fun aclGenpass(): String? = ops.aclGenpass().awaitFirstOrNull() + + override suspend fun aclGenpass(bits: Int): String? = ops.aclGenpass(bits).awaitFirstOrNull() + + override suspend fun aclGetuser(username: String): Map? = ops.aclGetuser(username).awaitFirstOrNull() + + override fun aclList(): Flow = ops.aclList().asFlow() + + override suspend fun aclLoad(): String? = ops.aclLoad().awaitFirstOrNull() + + override fun aclLog(): Flow> = ops.aclLog().asFlow() + + override fun aclLog(count: Int): Flow> = ops.aclLog(count).asFlow() + + override suspend fun aclLogReset(): String? = ops.aclLogReset().awaitFirstOrNull() + + override suspend fun aclSave(): String? = ops.aclSave().awaitFirstOrNull() + + override suspend fun aclSetuser(username: String, setuserArgs: AclSetuserArgs): String? = ops.aclSetuser(username, setuserArgs).awaitFirstOrNull() + + override suspend fun aclUsers(): List = ops.aclUsers().asFlow().toList() + + override suspend fun aclWhoami(): String? = ops.aclWhoami().awaitFirstOrNull() + +} + diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt index ce6d59ccdd..183d21ab28 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt @@ -29,6 +29,7 @@ import io.lettuce.core.cluster.api.coroutines.RedisClusterCoroutinesCommands @ExperimentalLettuceCoroutinesApi interface RedisCoroutinesCommands : BaseRedisCoroutinesCommands, + RedisAclCoroutinesCommands, RedisGeoCoroutinesCommands, RedisHashCoroutinesCommands, RedisHLLCoroutinesCommands, diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt index 8b320f516a..348def6db4 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt @@ -36,6 +36,7 @@ open class RedisCoroutinesCommandsImpl( internal val ops: RedisReactiveCommands ) : RedisCoroutinesCommands, RedisClusterCoroutinesCommands, BaseRedisCoroutinesCommands by BaseRedisCoroutinesCommandsImpl(ops), + RedisAclCoroutinesCommands by RedisAclCoroutinesCommandsImpl(ops), RedisGeoCoroutinesCommands by RedisGeoCoroutinesCommandsImpl(ops), RedisHashCoroutinesCommands by RedisHashCoroutinesCommandsImpl(ops), RedisHLLCoroutinesCommands by RedisHLLCoroutinesCommandsImpl(ops), diff --git a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt index 9e69abb6d2..2e9930117a 100644 --- a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt @@ -29,6 +29,7 @@ import io.lettuce.core.api.coroutines.* @ExperimentalLettuceCoroutinesApi interface RedisClusterCoroutinesCommands : BaseRedisCoroutinesCommands, + RedisAclCoroutinesCommands, RedisGeoCoroutinesCommands, RedisHashCoroutinesCommands, RedisHLLCoroutinesCommands, diff --git a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt index 5da3830970..29cf406abf 100644 --- a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt @@ -36,6 +36,7 @@ internal class RedisClusterCoroutinesCommandsImpl( internal val ops: RedisClusterReactiveCommands ) : RedisClusterCoroutinesCommands, BaseRedisCoroutinesCommands by BaseRedisCoroutinesCommandsImpl(ops), + RedisAclCoroutinesCommands by RedisAclCoroutinesCommandsImpl(ops), RedisGeoCoroutinesCommands by RedisGeoCoroutinesCommandsImpl(ops), RedisHashCoroutinesCommands by RedisHashCoroutinesCommandsImpl(ops), RedisHLLCoroutinesCommands by RedisHLLCoroutinesCommandsImpl(ops), diff --git a/src/main/templates/io/lettuce/core/api/RedisAclCommands.java b/src/main/templates/io/lettuce/core/api/RedisAclCommands.java new file mode 100644 index 0000000000..66d36ec2bf --- /dev/null +++ b/src/main/templates/io/lettuce/core/api/RedisAclCommands.java @@ -0,0 +1,147 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core; + +import io.lettuce.core.*; +import io.lettuce.core.protocol.CommandType; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * ${intent} for the ACL-API. + * + * @author Mark Paluch + * @author Mikhael Sokolov + * @since 6.1 + */ +public interface RedisAclCommands { + + /** + * The command shows the available ACL categories if called without arguments. + * + * @return List<AclCategory> a list of ACL categories or + */ + List aclCat(); + + /** + * The command shows all the Redis commands in the specified category. + * + * @param category the specified category + * @return List<CommandType> a list of commands inside a given category + */ + List aclCat(AclCategory category); + + /** + * Delete all the specified ACL users and terminate all the connections that are authenticated with such users. + * + * @param usernames the specified usernames + * @return Long The number of users that were deleted + */ + Long aclDeluser(String... usernames); + + /** + * The command generates a password. + * + * @return String bulk-string-reply 64 bytes string password representing 256 bits of pseudorandom data. + */ + String aclGenpass(); + + /** + * The command generates a password. + * + * @param bits amount of bits + * @return String bulk-string-reply N/4 bytes string password representing N bits of pseudorandom data. + */ + String aclGenpass(int bits); + + /** + * The command returns all the rules defined for an existing ACL user. + * + * @param username the specified username + * @return Map<String, Object> a map of ACL rule definitions for the user. + */ + Map aclGetuser(String username); + + /** + * The command shows the currently active ACL rules in the Redis server. + * + * @return List<String> a list of strings. + */ + List aclList(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), this command + * will reload the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. + * + * @return String simple-string-reply OK or error message. + */ + String aclLoad(); + + /** + * The command shows a list of recent ACL security events. + * + * @return List<Map<K,Object>> list of security events. + */ + List> aclLog(); + + /** + * The command shows a list of recent ACL security events. + * + * @param count max count of events + * @return List<Map<K, Object>> list of security events. + */ + List> aclLog(int count); + + /** + * The command clears ACL security events. + * + * @return String simple-string-reply OK if the security log was cleared. + */ + String aclLogReset(); + + /** + * When Redis is configured to use an ACL file (with the aclfile configuration option), + * this command will save the currently defined ACLs from the server memory to the ACL file. + * + * @return String simple-string-reply OK or error message. + */ + String aclSave(); + + /** + * Create an ACL user with the specified rules or modify the rules of an existing user. + * + * @param username the specified username + * @param rules rules + * @return String simple-string-reply OK or error message. + */ + String aclSetuser(String username, AclSetuserArgs setuserArgs); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return List<K> a list of usernames. + */ + List aclUsers(); + + /** + * The command shows a list of all the usernames of the currently configured users in the Redis ACL system. + * + * @return K bulk-string-reply the username of the current connection. + */ + String aclWhoami(); +} \ No newline at end of file diff --git a/src/test/java/io/lettuce/apigenerator/KotlinCompilationUnitFactory.java b/src/test/java/io/lettuce/apigenerator/KotlinCompilationUnitFactory.java index f2c4830931..1f38a37917 100644 --- a/src/test/java/io/lettuce/apigenerator/KotlinCompilationUnitFactory.java +++ b/src/test/java/io/lettuce/apigenerator/KotlinCompilationUnitFactory.java @@ -60,7 +60,7 @@ class KotlinCompilationUnitFactory { private static final Set NON_SUSPENDABLE_METHODS = LettuceSets.unmodifiableSet("isOpen", "flushCommands", "setAutoFlushCommands"); private static final Set SKIP_METHODS = LettuceSets.unmodifiableSet("BaseRedisCommands.reset", "getStatefulConnection"); - private static final Set FLOW_METHODS = LettuceSets.unmodifiableSet("dispatch", "geohash", "georadius", + private static final Set FLOW_METHODS = LettuceSets.unmodifiableSet("aclList", "aclLog", "dispatch", "geohash", "georadius", "georadiusbymember", "geosearch", "hgetall", "hkeys", "hmget", "hvals", "keys", "mget", "sdiff", "sinter", "smembers", "smismember", "sort", "srandmember", "sunion", "xclaim", "xpending", "xrange", "xread", "xreadgroup", "xrevrange", "zdiff", "zdiffWithScores", "zinter", "zinterWithScores", "zpopmax", "zpopmin", "zrange", @@ -77,7 +77,7 @@ class KotlinCompilationUnitFactory { static { Map resultSpec = new HashMap<>(); - resultSpec.put("hgetall", "Flow>"); + resultSpec.put("hgetall", "Flow>"); resultSpec.put("zmscore", "List"); RESULT_SPEC = resultSpec; } @@ -251,10 +251,10 @@ private String toKotlinType(Type type, boolean isFlowable, boolean isForceNonNul } else if (isFlowable) { fixedType = type .asString() - .replace("List", "Flow") - .replace("Set", "Flow") - .replace("Map", "Flow") - .replace("T", "Flow"); + .replaceFirst("List|Map|Set", "Flow") + .replace("T", "Flow") + .replace("Object", "Any") + .replace(",", ", "); } else { fixedType = type .asString() diff --git a/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java new file mode 100644 index 0000000000..3b1cdb504b --- /dev/null +++ b/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java @@ -0,0 +1,125 @@ +/* + * Copyright 2011-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core.commands; + +import io.lettuce.core.AclCategory; +import io.lettuce.core.AclSetuserArgs; +import io.lettuce.core.RedisCommandExecutionException; +import io.lettuce.core.TestSupport; +import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.protocol.CommandType; +import io.lettuce.test.LettuceExtension; +import io.lettuce.test.condition.EnabledOnCommand; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; + +import javax.inject.Inject; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author Mikhael Sokolov + */ +@ExtendWith(LettuceExtension.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@EnabledOnCommand("ACL") +public class AclCommandIntegrationTests extends TestSupport { + + private final RedisCommands redis; + + @Inject + protected AclCommandIntegrationTests(RedisCommands redis) { + this.redis = redis; + } + + @BeforeEach + void setUp() { + redis.flushall(); + redis.aclUsers().stream().filter(o -> !"default".equals(o)).forEach(redis::aclDeluser); + redis.aclLogReset(); + + } + + @Test + void aclCat() { + assertThat(redis.aclCat()).isNotEmpty(); + assertThat(redis.aclCat(AclCategory.SLOW)).isNotEmpty(); + } + + @Test + void aclDeluser() { + assertThat(redis.aclDeluser("non-existing")).isZero(); + } + + @Test + void aclGenpass() { + assertThat(redis.aclGenpass()).hasSize(64); + assertThat(redis.aclGenpass(128)).hasSize(32); + } + + @Test + void aclGetuser() { + assertThat(redis.aclGetuser("default")).hasFieldOrProperty("flags"); + } + + @Test + void aclLoad() { + assertThatThrownBy(redis::aclLoad).isInstanceOf(RedisCommandExecutionException.class).hasMessageContaining("ERR This Redis instance is not configured to use an ACL file."); + } + + @Test + void aclLog() { + assertThat(redis.aclLogReset()).isEqualTo("OK"); + assertThatThrownBy(() -> redis.auth("non-existing1", "foobar")); + assertThatThrownBy(() -> redis.auth("non-existing2", "foobar")); + assertThat(redis.aclLog()).hasSize(2).first().hasFieldOrProperty("reason"); + assertThat(redis.aclLog(1)).hasSize(1); + assertThat(redis.aclLogReset()).isEqualTo("OK"); + assertThat(redis.aclLog()).hasSize(0); + } + + @Test + void aclList() { + assertThat(redis.aclList()).hasSize(1).first().asString().contains("user default"); + } + + @Test + void aclSave() { + assertThatThrownBy(redis::aclSave).isInstanceOf(RedisCommandExecutionException.class).hasMessageContaining("ERR This Redis instance is not configured to use an ACL file."); + } + + @Test + void aclSetuser() { + assertThat(redis.aclDeluser("foo")).isNotNull(); + AclSetuserArgs args = AclSetuserArgs.Builder.on().addCommand(CommandType.GET).keyPattern("objects:*").addPassword("foobared"); + assertThat(redis.aclSetuser("foo", args)).isEqualTo("OK"); + assertThat(redis.aclGetuser("foo")).containsKey("commands").containsKey("passwords").containsKey("keys"); + assertThat(redis.aclDeluser("foo")).isNotNull(); + } + + @Test + void aclUsers() { + assertThat(redis.aclUsers()).hasSize(1).first().isEqualTo("default"); + } + + @Test + void aclWhoami() { + assertThat(redis.aclWhoami()).isEqualTo("default"); + } +}