ConsolidatedAccessControlListCommandsTest.java
package redis.clients.jedis.modules;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import io.redis.test.annotations.SinceRedisVersion;
import java.util.Locale;
import java.util.function.Consumer;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedClass;
import org.junit.jupiter.params.provider.MethodSource;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.RedisProtocol;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.bloom.RedisBloomProtocol.*;
import redis.clients.jedis.commands.ProtocolCommand;
import redis.clients.jedis.exceptions.JedisAccessControlException;
import redis.clients.jedis.json.JsonProtocol.JsonCommand;
import redis.clients.jedis.search.SearchProtocol.SearchCommand;
import redis.clients.jedis.search.schemafields.TextField;
import redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesCommand;
import redis.clients.jedis.util.SafeEncoder;
@SinceRedisVersion(value = "7.9.0")
@ParameterizedClass
@MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions")
public class ConsolidatedAccessControlListCommandsTest extends RedisModuleCommandsTestBase {
public static final String USER_NAME = "moduser";
public static final String USER_PASSWORD = "secret";
public ConsolidatedAccessControlListCommandsTest(RedisProtocol protocol) {
super(protocol);
}
@AfterEach
@Override
public void tearDown() throws Exception {
try {
jedis.aclDelUser(USER_NAME);
} catch (Exception e) { }
super.tearDown();
}
@Test
public void listACLCategoriesTest() {
assertThat(jedis.aclCat(),
Matchers.hasItems("bloom", "cuckoo", "cms", "topk", "tdigest",
"search", "timeseries", "json"));
}
@Test
public void grantBloomCommandTest() {
grantModuleCommandTest(BloomFilterCommand.RESERVE, client -> client.bfReserve("foo", 0.01, 10_000));
}
@Test
public void grantBloomCommandCatTest() {
grantModuleCommandCatTest("bloom", BloomFilterCommand.RESERVE, client -> client.bfReserve("foo", 0.01, 10_000));
}
@Test
public void grantCuckooCommandTest() {
grantModuleCommandTest(CuckooFilterCommand.RESERVE, client -> client.cfReserve("foo", 10_000));
}
@Test
public void grantCuckooCommandCatTest() {
grantModuleCommandCatTest("cuckoo", CuckooFilterCommand.RESERVE, client -> client.cfReserve("foo", 10_000));
}
@Test
public void grantCmsCommandTest() {
grantModuleCommandTest(CountMinSketchCommand.INITBYDIM, client -> client.cmsInitByDim("foo", 16, 4));
}
@Test
public void grantCmsCommandCatTest() {
grantModuleCommandCatTest("cms", CountMinSketchCommand.INITBYDIM, client -> client.cmsInitByDim("foo", 16, 4));
}
@Test
public void grantTopkCommandTest() {
grantModuleCommandTest(TopKCommand.RESERVE, client -> client.topkReserve("foo", 1000));
}
@Test
public void grantTopkCommandCatTest() {
grantModuleCommandCatTest("topk", TopKCommand.RESERVE, client -> client.topkReserve("foo", 1000));
}
@Test
public void grantTdigestCommandTest() {
grantModuleCommandTest(TDigestCommand.CREATE, client -> client.tdigestCreate("foo"));
}
@Test
public void grantTdigestCommandCatTest() {
grantModuleCommandCatTest("tdigest", TDigestCommand.CREATE, client -> client.tdigestCreate("foo"));
}
@Test
public void grantSearchCommandTest() {
grantModuleCommandTest(SearchCommand.CREATE,
client -> client.ftCreate("foo", TextField.of("bar")));
}
@Test
public void grantSearchCommandCatTest() {
grantModuleCommandCatTest("search", SearchCommand.CREATE,
client -> client.ftCreate("foo", TextField.of("bar")));
}
@Test
public void grantTimeseriesCommandTest() {
grantModuleCommandTest(TimeSeriesCommand.CREATE, client -> client.tsCreate("foo"));
}
@Test
public void grantTimeseriesCommandCatTest() {
grantModuleCommandCatTest("timeseries", TimeSeriesCommand.CREATE, client -> client.tsCreate("foo"));
}
@Test
public void grantJsonCommandTest() {
grantModuleCommandTest(JsonCommand.GET, client -> client.jsonGet("foo"));
}
@Test
public void grantJsonCommandCatTest() {
grantModuleCommandCatTest("json", JsonCommand.GET, client -> client.jsonGet("foo"));
}
private void grantModuleCommandTest(ProtocolCommand command, Consumer<UnifiedJedis> operation) {
// create and enable an user with permission to all keys but no commands
jedis.aclSetUser(USER_NAME, ">" + USER_PASSWORD, "on", "~*");
// client object with new user
try (UnifiedJedis client = new UnifiedJedis(hnp,
DefaultJedisClientConfig.builder().user(USER_NAME).password(USER_PASSWORD).build())) {
// user can't execute commands
JedisAccessControlException noperm = assertThrows(JedisAccessControlException.class,
() -> operation.accept(client), "Should throw a NOPERM exception");
assertThat(noperm.getMessage(), Matchers.oneOf(getNopermErrorMessage(false, command),
getNopermErrorMessage(true, command)));
// permit user to commands
jedis.aclSetUser(USER_NAME, "+" + SafeEncoder.encode(command.getRaw()));
// user can now execute commands
operation.accept(client);
}
}
private void grantModuleCommandCatTest(String category, ProtocolCommand command,
Consumer<UnifiedJedis> operation) {
// create and enable an user with permission to all keys but no commands
jedis.aclSetUser(USER_NAME, ">" + USER_PASSWORD, "on", "~*");
// client object with new user
try (UnifiedJedis client = new UnifiedJedis(hnp,
DefaultJedisClientConfig.builder().user(USER_NAME).password(USER_PASSWORD).build())) {
// user can't execute category commands
JedisAccessControlException noperm = assertThrows(JedisAccessControlException.class,
() -> operation.accept(client), "Should throw a NOPERM exception");
assertThat(noperm.getMessage(), Matchers.oneOf(getNopermErrorMessage(false, command),
getNopermErrorMessage(true, command)));
// permit user to category commands
jedis.aclSetUser(USER_NAME, "+@" + category);
// user can now execute category commands
operation.accept(client);
}
}
private static String getNopermErrorMessage(boolean commandNameUpperCase, ProtocolCommand protocolCommand) {
String command = SafeEncoder.encode(protocolCommand.getRaw());
return String.format("NOPERM User %s has no permissions to run the '%s' command",
USER_NAME, commandNameUpperCase ? command.toUpperCase(Locale.ENGLISH) : command.toLowerCase(Locale.ENGLISH));
}
}