Work on audit log redux

This commit is contained in:
Colin McDonald 2016-06-25 21:30:39 -04:00
parent f8bf433953
commit 2a5118ee3f
9 changed files with 131 additions and 63 deletions

View File

@ -2,22 +2,31 @@ package net.frozenorb.apiv3.auditLog;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.mongodb.async.SingleResultCallback; import com.mongodb.async.SingleResultCallback;
import io.vertx.ext.web.RoutingContext;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.actor.Actor; import net.frozenorb.apiv3.actor.Actor;
import net.frozenorb.apiv3.model.AuditLogEntry; import net.frozenorb.apiv3.model.AuditLogEntry;
import net.frozenorb.apiv3.model.User;
import java.util.Map; import java.util.Map;
import java.util.UUID;
@UtilityClass @UtilityClass
public class AuditLog { public class AuditLog {
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, SingleResultCallback<AuditLogEntry> callback) { public static void log(UUID performedBy, String performedByIp, RoutingContext ctx, AuditLogActionType actionType, SingleResultCallback<AuditLogEntry> callback) {
log(performedBy, performedByIp, actor, actionType, ImmutableMap.of(), callback); log(performedBy, performedByIp, ctx.get("actor"), ctx.request().remoteAddress().host(), actionType, ImmutableMap.of(), callback);
} }
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, Map<String, Object> actionData, SingleResultCallback<AuditLogEntry> callback) { public static void log(UUID performedBy, String performedByIp, RoutingContext ctx, AuditLogActionType actionType, Map<String, Object> actionData, SingleResultCallback<AuditLogEntry> callback) {
AuditLogEntry entry = new AuditLogEntry(performedBy, performedByIp, actor, actionType, actionData); log(performedBy, performedByIp, ctx.get("actor"), ctx.request().remoteAddress().host(), actionType, actionData, callback);
}
public static void log(UUID performedBy, String performedByIp, Actor actor, String actorIp, AuditLogActionType actionType, SingleResultCallback<AuditLogEntry> callback) {
log(performedBy, performedByIp, actor, actorIp, actionType, ImmutableMap.of(), callback);
}
public static void log(UUID performedBy, String performedByIp, Actor actor, String actorIp, AuditLogActionType actionType, Map<String, Object> actionData, SingleResultCallback<AuditLogEntry> callback) {
AuditLogEntry entry = new AuditLogEntry(performedBy, performedByIp, actor,actorIp, actionType, actionData);
entry.insert((ignored, error) -> { entry.insert((ignored, error) -> {
if (error != null) { if (error != null) {
callback.onResult(null, error); callback.onResult(null, error);

View File

@ -1,26 +1,49 @@
package net.frozenorb.apiv3.auditLog; package net.frozenorb.apiv3.auditLog;
import com.mongodb.async.SingleResultCallback; import com.mongodb.async.SingleResultCallback;
import lombok.Getter;
import net.frozenorb.apiv3.model.AuditLogEntry; import net.frozenorb.apiv3.model.AuditLogEntry;
public enum AuditLogActionType { public enum AuditLogActionType {
DELETE_PUNISHMENT { BANNED_ASN_CREATE(false),
BANNED_ASN_UPDATE(false),
BANNED_ASN_DELETE(false),
BANNED_CALL_CARRIER_CREATE(false),
BANNED_CALL_CARRIER_UPDATE(false),
BANNED_CALL_CARRIER_DELETE(false),
GRANT_CREATE(false),
GRANT_UPDATE(false),
GRANT_DELETE(false),
IP_BAN_CREATE(false),
IP_BAN_UPDATE(false),
IP_BAN_DELETE(false),
NOTIFICATION_TEMPLATE_CREATE(false),
NOTIFICATION_TEMPLATE_UPDATE(false),
NOTIFICATION_TEMPLATE_DELETE(false),
PUNISHMENT_CREATE(false),
PUNISHMENT_UPDATE(false),
PUNISHMENT_DELETE(false),
RANK_CREATE(false),
RANK_UPDATE(false),
RANK_DELETE(false),
SERVER_GROUP_CREATE(false),
SERVER_GROUP_UPDATE(false),
SERVER_GROUP_DELETE(false),
SERVER_CREATE(false),
SERVER_UPDATE(false),
SERVER_DELETE(false),
USER_CHANGE_PASSWORD(false),
USER_PASSWORD_RESET(false),
USER_REGISTER(false),
USER_SETUP_TOTP(false),
USER_VERIFY_TOTP(false);
@Override @Getter private boolean reversible;
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
callback.onResult(false, null);
}
}, AuditLogActionType(boolean reversible) {
DELETE_GRANT { this.reversible = reversible;
}
@Override
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
callback.onResult(false, null);
}
};
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) { public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
callback.onResult(null, new UnsupportedOperationException()); callback.onResult(null, new UnsupportedOperationException());

View File

@ -30,6 +30,10 @@ public final class AuditLogEntry {
@Getter private Instant performedAt; @Getter private Instant performedAt;
@Getter private String actorName; @Getter private String actorName;
@Getter private ActorType actorType; @Getter private ActorType actorType;
@Getter private String actorIp;
// We store 'reversible' in each object in case later on we go back and
// make something reversible (by storing more meta or such)
@Getter private boolean reversible;
@Getter private AuditLogActionType type; @Getter private AuditLogActionType type;
@Getter private Map<String, Object> metadata; @Getter private Map<String, Object> metadata;
@ -43,13 +47,15 @@ public final class AuditLogEntry {
private AuditLogEntry() {} // For Jackson private AuditLogEntry() {} // For Jackson
public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map<String, Object> metadata) { public AuditLogEntry(UUID user, String userIp, Actor actor, String actorIp, AuditLogActionType type, Map<String, Object> metadata) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();
this.user = user.getId(); this.user = user;
this.userIp = userIp; this.userIp = userIp;
this.performedAt = Instant.now(); this.performedAt = Instant.now();
this.actorName = actor.getName(); this.actorName = actor.getName();
this.actorType = actor.getType(); this.actorType = actor.getType();
this.actorIp = actorIp;
this.reversible = type.isReversible();
this.type = type; this.type = type;
this.metadata = ImmutableMap.copyOf(metadata); this.metadata = ImmutableMap.copyOf(metadata);
} }

View File

@ -18,33 +18,37 @@ public final class POSTAuditLog implements Handler<RoutingContext> {
User.findById(requestBody.getString("user"), (user, error) -> { User.findById(requestBody.getString("user"), (user, error) -> {
if (error != null) { if (error != null) {
ErrorUtils.respondInternalError(ctx, error); ErrorUtils.respondInternalError(ctx, error);
} else if (user == null) { return;
ErrorUtils.respondNotFound(ctx, "User", requestBody.getString("user"));
} else {
String userIp = requestBody.getString("userIp");
if (!IpUtils.isValidIp(userIp)) {
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
return;
}
AuditLogActionType type;
try {
type = AuditLogActionType.valueOf(requestBody.getString("type"));
} catch (IllegalArgumentException ignored) {
ErrorUtils.respondNotFound(ctx, "Audit log action type", requestBody.getString("type"));
return;
}
AuditLog.log(user, userIp, ctx.get("actor"), type, ctx.getBodyAsJson().getMap(), (auditLogEntry, error2) -> {
if (error2 != null) {
ErrorUtils.respondInternalError(ctx, error2);
} else {
APIv3.respondJson(ctx, auditLogEntry);
}
});
} }
if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", requestBody.getString("user"));
return;
}
String userIp = requestBody.getString("userIp");
if (!IpUtils.isValidIp(userIp)) {
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
return;
}
AuditLogActionType type;
try {
type = AuditLogActionType.valueOf(requestBody.getString("type"));
} catch (IllegalArgumentException ignored) {
ErrorUtils.respondNotFound(ctx, "Audit log action type", requestBody.getString("type"));
return;
}
AuditLog.log(user.getId(), userIp, ctx, type, requestBody.getJsonObject("metadata").getMap(), (auditLogEntry, error2) -> {
if (error2 != null) {
ErrorUtils.respondInternalError(ctx, error2);
} else {
APIv3.respondJson(ctx, auditLogEntry);
}
});
}); });
} }

View File

@ -49,10 +49,14 @@ public final class DELETEGrantsId implements Handler<RoutingContext> {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>(); BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
grant.delete(removedBy, reason, callback); grant.delete(removedBy, reason, callback);
callback.get(); callback.get();
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of(), blockingCallback); AuditLog.log(removedBy.getId(), requestBody.getString("removedByIp"), ctx, AuditLogActionType.GRANT_DELETE,ImmutableMap.of("grantId", grant.getId()), (ignored, error) -> {
blockingCallback.get(); if (error != null) {
APIv3.respondJson(ctx, grant); ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, grant);
}
});
} }
} }

View File

@ -1,9 +1,12 @@
package net.frozenorb.apiv3.route.grants; package net.frozenorb.apiv3.route.grants;
import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog;
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
import net.frozenorb.apiv3.model.Grant; import net.frozenorb.apiv3.model.Grant;
import net.frozenorb.apiv3.model.Rank; import net.frozenorb.apiv3.model.Rank;
import net.frozenorb.apiv3.model.ServerGroup; import net.frozenorb.apiv3.model.ServerGroup;
@ -79,7 +82,18 @@ public final class POSTGrants implements Handler<RoutingContext> {
BlockingCallback<Void> callback = new BlockingCallback<>(); BlockingCallback<Void> callback = new BlockingCallback<>();
grant.insert(callback); grant.insert(callback);
callback.get(); callback.get();
APIv3.respondJson(ctx, grant);
if (addedBy != null) {
AuditLog.log(addedBy.getId(), requestBody.getString("addedByIp"), ctx, AuditLogActionType.GRANT_CREATE, ImmutableMap.of("grantId", grant.getId()), (ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, grant);
}
});
} else {
APIv3.respondJson(ctx, grant);
}
} }
} }

View File

@ -50,7 +50,7 @@ public final class DELETEIpBansId implements Handler<RoutingContext> {
ipBan.delete(removedBy, reason, callback); ipBan.delete(removedBy, reason, callback);
callback.get(); callback.get();
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>(); BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback); AuditLog.log(removedBy.getId(), requestBody.getString("removedByIp"), ctx, AuditLogActionType.IP_BAN_DELETE, ImmutableMap.of("punishmentId", ipBan.getId()), blockingCallback);
blockingCallback.get(); blockingCallback.get();
APIv3.respondJson(ctx, ipBan); APIv3.respondJson(ctx, ipBan);
} }

View File

@ -50,7 +50,7 @@ public final class DELETEPunishmentsId implements Handler<RoutingContext> {
punishment.delete(removedBy, reason, removeCallback); punishment.delete(removedBy, reason, removeCallback);
removeCallback.get(); removeCallback.get();
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>(); BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback); AuditLog.log(removedBy.getId(), requestBody.getString("removedByIp"), ctx, AuditLogActionType.PUNISHMENT_DELETE, ImmutableMap.of("punishmentId", punishment.getId()), blockingCallback);
blockingCallback.get(); blockingCallback.get();
APIv3.respondJson(ctx, punishment); APIv3.respondJson(ctx, punishment);
} }

View File

@ -50,21 +50,29 @@ public final class DELETEUsersIdActivePunishment implements Handler<RoutingConte
BlockingCallback<List<Punishment>> callback = new BlockingCallback<>(); BlockingCallback<List<Punishment>> callback = new BlockingCallback<>();
Punishment.findByUserAndType(target, ImmutableSet.of(type), callback); Punishment.findByUserAndType(target, ImmutableSet.of(type), callback);
Punishment activePunishment = null;
for (Punishment punishment : callback.get()) { for (Punishment punishment : callback.get()) {
if (punishment.isActive()) { if (punishment.isActive()) {
BlockingCallback<UpdateResult> punishmentCallback = new BlockingCallback<>(); activePunishment = punishment;
punishment.delete(removedBy, reason, punishmentCallback); break;
punishmentCallback.get();
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback);
blockingCallback.get();
APIv3.respondJson(ctx, punishment);
return;
} }
} }
ErrorUtils.respondGeneric(ctx, 404, "User provided has no active punishments"); if (activePunishment == null) {
ErrorUtils.respondGeneric(ctx, 404, "User provided has no active punishments");
return;
}
BlockingCallback<UpdateResult> punishmentCallback = new BlockingCallback<>();
activePunishment.delete(removedBy, reason, punishmentCallback);
punishmentCallback.get();
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
AuditLog.log(removedBy.getId(), requestBody.getString("removedByIp"), ctx, AuditLogActionType.PUNISHMENT_DELETE, ImmutableMap.of("punishmentId", activePunishment.getId()), blockingCallback);
blockingCallback.get();
APIv3.respondJson(ctx, activePunishment);
} }
} }