Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for Redis 6 ACL - Issue #2035 #2077

Merged
merged 17 commits into from
Mar 14, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion src/main/java/redis/clients/jedis/BinaryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class BinaryClient extends Connection {

private boolean isInMulti;

private String user;
private String password;

private int db;
Expand Down Expand Up @@ -86,6 +87,8 @@ private byte[][] joinParameters(byte[] first, byte[] second, byte[][] rest) {
return result;
}

public void setUser(final String user) { this.user = user; }

public void setPassword(final String password) {
this.password = password;
}
Expand All @@ -98,7 +101,10 @@ public void setDb(int db) {
public void connect() {
if (!isConnected()) {
super.connect();
if (password != null) {
if (user != null) {
auth(user, password);
getStatusCodeReply();
} else if (password != null) {
auth(password);
getStatusCodeReply();
}
Expand Down Expand Up @@ -585,6 +591,12 @@ public void auth(final String password) {
sendCommand(AUTH, password);
}

public void auth(final String user, final String password) {
setUser(user);
setPassword(password);
sendCommand(AUTH, user, password);
}

public void subscribe(final byte[]... channels) {
sendCommand(SUBSCRIBE, channels);
}
Expand Down Expand Up @@ -1244,6 +1256,36 @@ private ArrayList<byte[]> convertScoreMembersToByteArrays(final Map<byte[], Doub
return args;
}

public void aclWhoAmi() { sendCommand(ACL, Keyword.WHOAMI.raw); }
sazzad16 marked this conversation as resolved.
Show resolved Hide resolved

public void aclGenPass() { sendCommand(ACL, Keyword.GENPASS.raw); }

public void aclList() { sendCommand(ACL, Keyword.LIST.raw); }

public void aclUsers() { sendCommand(ACL, Keyword.USERS.raw); }

public void aclCat() { sendCommand(ACL, Keyword.CAT.raw); }

public void aclCat(final byte[] category) {
sendCommand(ACL, Keyword.CAT.raw, category);
}

public void aclSetUser(final byte[] name) {
sendCommand(ACL, Keyword.SETUSER.raw, name);
}

public void aclGetUser(final byte[] name) {
sendCommand(ACL, Keyword.GETUSER.raw, name);
}

public void aclSetUser(final byte[] name, byte[][] parameters) {
sendCommand(ACL, joinParameters(Keyword.SETUSER.raw,name, parameters));
}

public void aclDelUser(final byte[] name) {
sendCommand(ACL, Keyword.DELUSER.raw, name);
}

private List<byte[]> convertGeoCoordinateMapToByteArrays(
final Map<byte[], GeoCoordinate> memberCoordinateMap) {
List<byte[]> args = new ArrayList<byte[]>(memberCoordinateMap.size() * 3);
Expand Down
85 changes: 85 additions & 0 deletions src/main/java/redis/clients/jedis/BinaryJedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -2229,6 +2229,21 @@ public String auth(final String password) {
return client.getStatusCodeReply();
}

/**
* Request for authentication with a Redis Server that is using ACL where user are authenticated with
* username and password.
* See https://redis.io/topics/acl
* @param user
* @param password
* @return
*/
@Override
public String auth(final String user, final String password) {
checkIsInMultiOrPipeline();
client.auth(user, password);
return client.getStatusCodeReply();
}

public Pipeline pipelined() {
pipeline = new Pipeline();
pipeline.setClient(client);
Expand Down Expand Up @@ -3533,6 +3548,76 @@ public byte[] memoryDoctorBinary() {
return client.getBinaryBulkReply();
}

@Override
public byte[] aclWhoAmIBinary() {
checkIsInMultiOrPipeline();
client.aclWhoAmi();
return client.getBinaryBulkReply();
}

@Override
public byte[] aclGenPassBinary() {
checkIsInMultiOrPipeline();
client.aclGenPass();
return client.getBinaryBulkReply();
}

@Override
public List<byte[]> aclListBinary() {
checkIsInMultiOrPipeline();
client.aclList();
return client.getBinaryMultiBulkReply();
}

@Override
public List<byte[]> aclUsersBinary() {
checkIsInMultiOrPipeline();
client.aclUsers();
return client.getBinaryMultiBulkReply();
}

@Override
public UserACL aclGetUser(byte[] name) {
checkIsInMultiOrPipeline();
client.aclGetUser(name);
return BuilderFactory.USER_ACL.build(client.getObjectMultiBulkReply());
}

@Override
public String aclSetUser(byte[] name) {
checkIsInMultiOrPipeline();
client.aclSetUser(name);
return client.getStatusCodeReply();
}

@Override
public String aclSetUser(byte[] name, byte[]... keys) {
checkIsInMultiOrPipeline();
client.aclSetUser(name, keys);
return client.getStatusCodeReply();
}

@Override
public Long aclDelUser(byte[] name) {
checkIsInMultiOrPipeline();
client.aclDelUser(name);
return client.getIntegerReply();
}

@Override
public List<byte[]> aclCatBinary() {
checkIsInMultiOrPipeline();
client.aclCat();
return client.getBinaryMultiBulkReply();
}

@Override
public List<byte[]> aclCat(byte[] category) {
checkIsInMultiOrPipeline();
client.aclCat(category);
return client.getBinaryMultiBulkReply();
}

@Override
public String clientKill(final byte[] ipPort) {
checkIsInMultiOrPipeline();
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/redis/clients/jedis/BuilderFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,52 @@ public String toString() {
}
};

/**
* Create a UserACL object from the ACL GETUSER < > result
*/
public static final Builder<UserACL> USER_ACL = new Builder<UserACL>() {
@Override
public UserACL build(Object data) {
if (data == null) {
return null;
}

List<List<Object>> objectList = (List<List<Object>>) data;
if (objectList.isEmpty()) { return null; }

UserACL userACL = new UserACL();

// flags
List<Object> flags = objectList.get(1);
for (Object f : flags) {
userACL.addFlag(SafeEncoder.encode((byte[]) f));
};

// passwords
List<Object> passwords = objectList.get(3);
for (Object p : passwords) {
userACL.addPassword(SafeEncoder.encode((byte[]) p));
};

// commands
userACL.setCommands(SafeEncoder.encode((byte[]) (Object) objectList.get(5)));

// keys
List<Object> keys = objectList.get(7);
for (Object k : keys) {
userACL.addKey(SafeEncoder.encode((byte[]) k));
};

return userACL;
}

@Override
public String toString() {
return "UserACL";
}

};

public static final Builder<List<Long>> LONG_LIST = new Builder<List<Long>>() {
@Override
@SuppressWarnings("unchecked")
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/redis/clients/jedis/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,26 @@ public void moduleUnload(final String name) {
moduleUnload(SafeEncoder.encode(name));
}

public void aclGetUser(final String name) {
aclGetUser(SafeEncoder.encode(name));
}

public void aclSetUser(final String name) {
aclSetUser(SafeEncoder.encode(name));
}

public void aclSetUser(String name, String... parameters) {
aclSetUser(SafeEncoder.encode(name), SafeEncoder.encodeMany(parameters));
}

public void aclCat(final String category) {
aclCat(SafeEncoder.encode(category));
}

public void aclDelUser(final String name) {
aclDelUser(SafeEncoder.encode(name));
}

private HashMap<byte[], Double> convertScoreMembersToBinary(final Map<String, Double> scoreMembers) {
HashMap<byte[], Double> binaryScoreMembers = new HashMap<byte[], Double>();
for (Entry<String, Double> entry : scoreMembers.entrySet()) {
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/redis/clients/jedis/Jedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -3642,6 +3642,65 @@ public List<Module> moduleList() {
return BuilderFactory.MODULE_LIST.build(client.getObjectMultiBulkReply());
}

@Override
public String aclSetUser(final String name) {
client.aclSetUser(name);
return client.getStatusCodeReply();
}

public String aclSetUser(String name, String... params) {
client.aclSetUser(name, params);
return client.getStatusCodeReply();
}

@Override
public Long aclDelUser(final String name) {
client.aclDelUser(name);
return client.getIntegerReply();
}

@Override
public UserACL aclGetUser(final String name) {
client.aclGetUser(name);
return BuilderFactory.USER_ACL.build(client.getObjectMultiBulkReply());
}

@Override
public List<String> aclUsers() {
client.aclUsers();
return BuilderFactory.STRING_LIST.build(client.getObjectMultiBulkReply());
}

@Override
public List<String> aclList() {
client.aclList();
return client.getMultiBulkReply();
}

@Override
public String aclWhoAmI() {
client.aclWhoAmi();
return client.getStatusCodeReply();
}

@Override
public List<String> aclCat() {
client.aclCat();
return BuilderFactory.STRING_LIST.build(client.getObjectMultiBulkReply());
}

@Override
public List<String> aclCat(String category) {
client.aclCat(category);
return BuilderFactory.STRING_LIST.build(client.getObjectMultiBulkReply());
}

@Override
public String aclGenPass() {
client.aclGenPass();
return client.getStatusCodeReply();
}

@Override
public List<Long> bitfield(final String key, final String...arguments) {
checkIsInMultiOrPipeline();
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/redis/clients/jedis/JedisFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class JedisFactory implements PooledObjectFactory<Jedis> {
private final AtomicReference<HostAndPort> hostAndPort = new AtomicReference<HostAndPort>();
private final int connectionTimeout;
private final int soTimeout;
private final String user;
private final String password;
private final int database;
private final String clientName;
Expand All @@ -36,13 +37,28 @@ class JedisFactory implements PooledObjectFactory<Jedis> {
false, null, null, null);
}

JedisFactory(final String host, final int port, final int connectionTimeout,
final int soTimeout, final String user, final String password, final int database, final String clientName) {
this(host, port, connectionTimeout, soTimeout, password, database, clientName,
false, null, null, null);
}

JedisFactory(final String host, final int port, final int connectionTimeout,
final int soTimeout, final String password, final int database, final String clientName,
final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
final HostnameVerifier hostnameVerifier) {
this(host, port, connectionTimeout, soTimeout, null, password, database, clientName,
false, null, null, null);
}

JedisFactory(final String host, final int port, final int connectionTimeout,
final int soTimeout, final String user, final String password, final int database, final String clientName,
final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
final HostnameVerifier hostnameVerifier) {
this.hostAndPort.set(new HostAndPort(host, port));
this.connectionTimeout = connectionTimeout;
this.soTimeout = soTimeout;
this.user = user;
this.password = password;
this.database = database;
this.clientName = clientName;
Expand All @@ -68,6 +84,7 @@ class JedisFactory implements PooledObjectFactory<Jedis> {
this.hostAndPort.set(new HostAndPort(uri.getHost(), uri.getPort()));
this.connectionTimeout = connectionTimeout;
this.soTimeout = soTimeout;
this.user = JedisURIHelper.getUser(uri);
this.password = JedisURIHelper.getPassword(uri);
this.database = JedisURIHelper.getDBIndex(uri);
this.clientName = clientName;
Expand Down Expand Up @@ -115,7 +132,9 @@ public PooledObject<Jedis> makeObject() throws Exception {

try {
jedis.connect();
if (password != null) {
if (user != null) {
jedis.auth(user, password);
} else if (password != null) {
jedis.auth(password);
}
if (database != 0) {
Expand Down
Loading