Audit support for new RESTFul API.
Allow generating Audit events related to RESTFul API execution. Structure of the AuditEvent has been extended to support the new name-multivalue pairs used in the new API. This is breaking compatibility with the 2.5 API as it changes the params data type, this is needed anyway IMHO as the previous list of Objects was not providing all the necessary information of "what relates to what" in terms of parameters info. Existing support for SSH and JSON-RPC events have been adapted in order to fit into the new name-multivalue syntax: this allow a generic audit plug-in to capture all parameters regardless of where they have been generated. Change-Id: Ifb50dbff4eef1326b8199ea3c7324a981579547c Signed-off-by: Luca Milanesio <luca.milanesio@gmail.com>
This commit is contained in:

committed by
Shawn Pearce

parent
9dd764a380
commit
7a0ba5b558
@@ -196,7 +196,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
|
||||
|
||||
private void log(final int rc) {
|
||||
if (logged.compareAndSet(false, true)) {
|
||||
log.onExecute(rc);
|
||||
log.onExecute(cmd, rc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,9 +14,11 @@
|
||||
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.audit.AuditEvent;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gerrit.audit.AuditService;
|
||||
import com.google.gerrit.audit.SshAuditEvent;
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.PeerDaemonUser;
|
||||
@@ -42,7 +44,6 @@ import org.eclipse.jgit.util.QuotedString;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
@@ -101,7 +102,7 @@ class SshLog implements LifecycleListener {
|
||||
|
||||
void onLogin() {
|
||||
async.append(log("LOGIN FROM " + session.get().getRemoteAddressAsString()));
|
||||
audit(context.get(), "0", "LOGIN", new String[] {});
|
||||
audit(context.get(), "0", "LOGIN");
|
||||
}
|
||||
|
||||
void onAuthFail(final SshSession sd) {
|
||||
@@ -127,18 +128,14 @@ class SshLog implements LifecycleListener {
|
||||
}
|
||||
|
||||
async.append(event);
|
||||
audit(null, "FAIL", "AUTH", new String[] {sd.getRemoteAddressAsString()});
|
||||
audit(null, "FAIL", "AUTH");
|
||||
}
|
||||
|
||||
void onExecute(int exitValue) {
|
||||
void onExecute(DispatchCommand dcmd, int exitValue) {
|
||||
final Context ctx = context.get();
|
||||
ctx.finished = System.currentTimeMillis();
|
||||
|
||||
final String commandLine = ctx.getCommandLine();
|
||||
String cmd = QuotedString.BOURNE.quote(commandLine);
|
||||
if (cmd == commandLine) {
|
||||
cmd = "'" + commandLine + "'";
|
||||
}
|
||||
String cmd = extractWhat(dcmd);
|
||||
|
||||
final LoggingEvent event = log(cmd);
|
||||
event.setProperty(P_WAIT, (ctx.started - ctx.created) + "ms");
|
||||
@@ -165,19 +162,54 @@ class SshLog implements LifecycleListener {
|
||||
event.setProperty(P_STATUS, status);
|
||||
|
||||
async.append(event);
|
||||
audit(context.get(), status, getCommand(commandLine),
|
||||
CommandFactoryProvider.split(commandLine));
|
||||
audit(context.get(), status, dcmd);
|
||||
}
|
||||
|
||||
private String getCommand(String commandLine) {
|
||||
commandLine = commandLine.trim();
|
||||
int spacePos = commandLine.indexOf(' ');
|
||||
return (spacePos > 0 ? commandLine.substring(0, spacePos):commandLine);
|
||||
private Multimap<String, ?> extractParameters(DispatchCommand dcmd) {
|
||||
String[] cmdArgs = dcmd.getArguments();
|
||||
String paramName = null;
|
||||
int argPos = 0;
|
||||
Multimap<String, String> parms = ArrayListMultimap.create();
|
||||
for (int i = 2; i < cmdArgs.length; i++) {
|
||||
String arg = cmdArgs[i];
|
||||
// -- stop parameters parsing
|
||||
if (arg.equals("--")) {
|
||||
for (i++; i < cmdArgs.length; i++) {
|
||||
parms.put("$" + argPos++, cmdArgs[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// --param=value
|
||||
int eqPos = arg.indexOf('=');
|
||||
if (arg.startsWith("--") && eqPos > 0) {
|
||||
parms.put(arg.substring(0, eqPos), arg.substring(eqPos + 1));
|
||||
continue;
|
||||
}
|
||||
// -p value or --param value
|
||||
if (arg.startsWith("-")) {
|
||||
if (paramName != null) {
|
||||
parms.put(paramName, null);
|
||||
}
|
||||
paramName = arg;
|
||||
continue;
|
||||
}
|
||||
// value
|
||||
if (paramName == null) {
|
||||
parms.put("$" + argPos++, arg);
|
||||
} else {
|
||||
parms.put(paramName, arg);
|
||||
paramName = null;
|
||||
}
|
||||
}
|
||||
if (paramName != null) {
|
||||
parms.put(paramName, null);
|
||||
}
|
||||
return parms;
|
||||
}
|
||||
|
||||
void onLogout() {
|
||||
async.append(log("LOGOUT"));
|
||||
audit(context.get(), "0", "LOGOUT", new String[] {});
|
||||
audit(context.get(), "0", "LOGOUT");
|
||||
}
|
||||
|
||||
private LoggingEvent log(final String msg) {
|
||||
@@ -416,21 +448,28 @@ class SshLog implements LifecycleListener {
|
||||
}
|
||||
}
|
||||
|
||||
void audit(Context ctx, Object result, String commandName, String[] args) {
|
||||
void audit(Context ctx, Object result, String cmd) {
|
||||
final String sid = extractSessionId(ctx);
|
||||
final long created = extractCreated(ctx);
|
||||
final String what = extractWhat(commandName, args);
|
||||
auditService.dispatch(new AuditEvent(sid, extractCurrentUser(ctx), "ssh:"
|
||||
+ what, created, Arrays.asList(args), result));
|
||||
auditService.dispatch(new SshAuditEvent(sid, extractCurrentUser(ctx), cmd,
|
||||
created, null, result));
|
||||
}
|
||||
|
||||
private String extractWhat(String commandName, String[] args) {
|
||||
String result = commandName;
|
||||
if ("gerrit".equals(commandName)) {
|
||||
if (args.length > 1)
|
||||
result = "gerrit"+"."+args[1];
|
||||
void audit(Context ctx, Object result, DispatchCommand cmd) {
|
||||
final String sid = extractSessionId(ctx);
|
||||
final long created = extractCreated(ctx);
|
||||
auditService.dispatch(new SshAuditEvent(sid, extractCurrentUser(ctx),
|
||||
extractWhat(cmd), created, extractParameters(cmd), result));
|
||||
}
|
||||
|
||||
private String extractWhat(DispatchCommand dcmd) {
|
||||
String commandName = dcmd.getCommandName();
|
||||
String[] args = dcmd.getArguments();
|
||||
if (args.length > 1) {
|
||||
return commandName + "." + args[1];
|
||||
} else {
|
||||
return commandName;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private long extractCreated(final Context ctx) {
|
||||
|
Reference in New Issue
Block a user