Skip to content

Commit

Permalink
add support for ACL LOG command (#2302)
Browse files Browse the repository at this point in the history
* add support for ACL LOG command - issue #2170

* fix code after first review of PR - issue #2170

* more binary method, and better loops after review of the for issue #2170

Authored-by: Tugdual Grall <tugdual@gmail.com>
Co-authored-by: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com>
  • Loading branch information
sazzad16 authored Dec 7, 2020
1 parent 5525669 commit 6b419d5
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 1 deletion.
110 changes: 110 additions & 0 deletions src/main/java/redis/clients/jedis/AccessControlLogEntry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package redis.clients.jedis;

import java.io.Serializable;
import java.util.*;

/**
* This class holds information about an Access Control Log entry (returned by ACL LOG command)
* They can be access via getters.
* For future purpose there is also {@link #getlogEntry} method
* that returns a generic {@code Map} - in case where more info is returned from a server
*
*/
public class AccessControlLogEntry implements Serializable {

private static final long serialVersionUID = 1L;

public static final String COUNT = "count";
public static final String REASON = "reason";
public static final String CONTEXT = "context";
public static final String OBJECT = "object";
public static final String USERNAME = "username";
public static final String AGE_SECONDS = "age-seconds";
public static final String CLIENT_INFO = "client-info";

private long count;
private final String reason;
private final String context;
private final String object;
private final String username;
private final String ageSeconds;
private final Map<String,String> clientInfo;
private final Map<String,Object> logEntry;


public AccessControlLogEntry(Map<String, Object> map) {
count = (long)map.get(COUNT);
reason = (String)map.get(REASON);
context = (String)map.get(CONTEXT);
object = (String)map.get(OBJECT);
username = (String)map.get(USERNAME);
ageSeconds = (String)map.get(AGE_SECONDS);
clientInfo = getMapFromRawClientInfo((String)map.get(CLIENT_INFO));
logEntry = map;
}

public long getCount() {
return count;
}

public String getReason() {
return reason;
}

public String getContext() {
return context;
}

public String getObject() {
return object;
}

public String getUsername() {
return username;
}

public String getAgeSeconds() {
return ageSeconds;
}

public Map<String, String> getClientInfo() {
return clientInfo;
}

/**
* @return Generic map containing all key-value pairs returned by the server
*/
public Map<String,Object> getlogEntry() {
return logEntry;
}

/**
* Convert the client-info string into a Map of String.
* When the value is empty, the value in the map is set to an empty string
* The key order is maintained to reflect the string return by Redis
* @param clientInfo
* @return A Map with all client info
*/
private Map<String,String> getMapFromRawClientInfo( String clientInfo) {
String[] entries = clientInfo.split(" ");
Map<String,String> clientInfoMap = new LinkedHashMap<>(entries.length);
for (String entry : entries) {
String[] kvArray = entry.split("=");
clientInfoMap.put(kvArray[0], (kvArray.length ==2)?kvArray[1]:"" );
}
return clientInfoMap;
}

@Override
public String toString() {
return "AccessControlLogEntry{" +
"count=" + count +
", reason='" + reason + '\'' +
", context='" + context + '\'' +
", object='" + object + '\'' +
", username='" + username + '\'' +
", ageSeconds='" + ageSeconds + '\'' +
", clientInfo=" + clientInfo +
'}';
}
}
12 changes: 12 additions & 0 deletions src/main/java/redis/clients/jedis/BinaryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,18 @@ public void aclCat(final byte[] category) {
sendCommand(ACL, Keyword.CAT.raw, category);
}

public void aclLog() {
sendCommand(ACL, Keyword.LOG.raw);
}

public void aclLog(int limit) {
sendCommand(ACL, Keyword.LOG.raw, toByteArray(limit));
}

public void aclLog(final byte[] option) {
sendCommand(ACL, Keyword.LOG.raw, option);
}

public void aclSetUser(final byte[] name) {
sendCommand(ACL, Keyword.SETUSER.raw, name);
}
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/redis/clients/jedis/BinaryJedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -3779,6 +3779,26 @@ public List<byte[]> aclCat(byte[] category) {
return client.getBinaryMultiBulkReply();
}

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

@Override
public List<byte[]> aclLogBinary(int limit) {
checkIsInMultiOrPipeline();
client.aclLog(limit);
return client.getBinaryMultiBulkReply();
}
@Override
public byte[] aclLog(byte[] options) {
checkIsInMultiOrPipeline();
client.aclLog(options);
return client.getBinaryBulkReply();
}

@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 @@ -522,6 +522,52 @@ public String toString() {

};

/**
* Create an Access Control Log Entry
* Result of ACL LOG command
*/
public static final Builder<List<AccessControlLogEntry>> ACCESS_CONTROL_LOG_ENTRY_LIST = new Builder<List<AccessControlLogEntry>>() {

private final Map<String,Builder> mappingFunctions = createDecoderMap();

private Map<String, Builder> createDecoderMap() {

Map<String,Builder> tempMappingFunctions = new HashMap<>();
tempMappingFunctions.put(AccessControlLogEntry.COUNT ,LONG);
tempMappingFunctions.put(AccessControlLogEntry.REASON ,STRING);
tempMappingFunctions.put(AccessControlLogEntry.CONTEXT ,STRING);
tempMappingFunctions.put(AccessControlLogEntry.OBJECT ,STRING);
tempMappingFunctions.put(AccessControlLogEntry.USERNAME,STRING);
tempMappingFunctions.put(AccessControlLogEntry.AGE_SECONDS,STRING);
tempMappingFunctions.put(AccessControlLogEntry.CLIENT_INFO,STRING);

return tempMappingFunctions;
}

@Override
public List<AccessControlLogEntry> build(Object data) {

if (null == data) {
return null;
}

List<AccessControlLogEntry> list = new ArrayList<>();
List<List<Object>> logEntries = (List<List<Object>>)data;
for (List<Object> logEntryData : logEntries) {
Iterator<Object> logEntryDataIterator = logEntryData.iterator();
AccessControlLogEntry accessControlLogEntry = new AccessControlLogEntry(
createMapFromDecodingFunctions(logEntryDataIterator,mappingFunctions));
list.add(accessControlLogEntry);
}
return list;
}

@Override
public String toString() {
return "List<AccessControlLogEntry>";
}
};

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

public void aclLog(final String options) {
aclLog(SafeEncoder.encode(options));
}

public void aclDelUser(final String name) {
aclDelUser(SafeEncoder.encode(name));
}
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/redis/clients/jedis/Jedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -3820,6 +3820,24 @@ public List<String> aclCat(String category) {
return BuilderFactory.STRING_LIST.build(client.getObjectMultiBulkReply());
}

@Override
public List<AccessControlLogEntry> aclLog() {
client.aclLog();
return BuilderFactory.ACCESS_CONTROL_LOG_ENTRY_LIST.build(client.getObjectMultiBulkReply());
}

@Override
public List<AccessControlLogEntry> aclLog(int limit) {
client.aclLog(limit);
return BuilderFactory.ACCESS_CONTROL_LOG_ENTRY_LIST.build(client.getObjectMultiBulkReply());
}

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

@Override
public String aclGenPass() {
client.aclGenPass();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/redis/clients/jedis/Protocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ public static enum Keyword {
GETNAME, SETNAME, LIST, MATCH, COUNT, PING, PONG, UNLOAD, REPLACE, KEYS, PAUSE, DOCTOR,
BLOCK, NOACK, STREAMS, KEY, CREATE, MKSTREAM, SETID, DESTROY, DELCONSUMER, MAXLEN, GROUP,
ID, IDLE, TIME, RETRYCOUNT, FORCE, USAGE, SAMPLES, STREAM, GROUPS, CONSUMERS, HELP, FREQ,
SETUSER, GETUSER, DELUSER, WHOAMI, CAT, GENPASS, USERS;
SETUSER, GETUSER, DELUSER, WHOAMI, CAT, GENPASS, USERS, LOG;

/**
* @deprecated This will be private in future. Use {@link #getRaw()}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import redis.clients.jedis.AccessControlLogEntry;
import redis.clients.jedis.AccessControlUser;
import redis.clients.jedis.params.MigrateParams;
import redis.clients.jedis.params.ClientKillParams;
Expand Down Expand Up @@ -74,5 +75,11 @@ public interface AdvancedBinaryJedisCommands {

List<byte[]> aclCat(byte[] category);

List<byte[]> aclLogBinary();

List<byte[]> aclLogBinary(int limit);

byte[] aclLog(byte[] options);

// TODO: Implements ACL LOAD/SAVE commands
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package redis.clients.jedis.commands;

import java.util.List;
import java.util.Map;

import redis.clients.jedis.AccessControlLogEntry;
import redis.clients.jedis.AccessControlUser;
import redis.clients.jedis.params.MigrateParams;
import redis.clients.jedis.params.ClientKillParams;
Expand Down Expand Up @@ -74,5 +76,11 @@ public interface AdvancedJedisCommands {

List<String> aclCat(String category);

List<AccessControlLogEntry> aclLog();

List<AccessControlLogEntry> aclLog(int limit);

String aclLog(String options);

// TODO: Implements ACL LOAD/SAVE commands
}
Loading

0 comments on commit 6b419d5

Please sign in to comment.