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.mongodb.async.SingleResultCallback;
import io.vertx.ext.web.RoutingContext;
import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.actor.Actor;
import net.frozenorb.apiv3.model.AuditLogEntry;
import net.frozenorb.apiv3.model.User;
import java.util.Map;
import java.util.UUID;
@UtilityClass
public class AuditLog {
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, SingleResultCallback<AuditLogEntry> callback) {
log(performedBy, performedByIp, actor, actionType, ImmutableMap.of(), callback);
public static void log(UUID performedBy, String performedByIp, RoutingContext ctx, AuditLogActionType actionType, SingleResultCallback<AuditLogEntry> 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) {
AuditLogEntry entry = new AuditLogEntry(performedBy, performedByIp, actor, actionType, actionData);
public static void log(UUID performedBy, String performedByIp, RoutingContext ctx, AuditLogActionType actionType, Map<String, Object> actionData, SingleResultCallback<AuditLogEntry> callback) {
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) -> {
if (error != null) {
callback.onResult(null, error);

View File

@ -1,26 +1,49 @@
package net.frozenorb.apiv3.auditLog;
import com.mongodb.async.SingleResultCallback;
import lombok.Getter;
import net.frozenorb.apiv3.model.AuditLogEntry;
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
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
callback.onResult(false, null);
}
@Getter private boolean reversible;
},
DELETE_GRANT {
@Override
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
callback.onResult(false, null);
}
};
AuditLogActionType(boolean reversible) {
this.reversible = reversible;
}
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
callback.onResult(null, new UnsupportedOperationException());

View File

@ -30,6 +30,10 @@ public final class AuditLogEntry {
@Getter private Instant performedAt;
@Getter private String actorName;
@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 Map<String, Object> metadata;
@ -43,13 +47,15 @@ public final class AuditLogEntry {
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.user = user.getId();
this.user = user;
this.userIp = userIp;
this.performedAt = Instant.now();
this.actorName = actor.getName();
this.actorType = actor.getType();
this.actorIp = actorIp;
this.reversible = type.isReversible();
this.type = type;
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) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else if (user == null) {
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);
}
});
return;
}
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<>();
grant.delete(removedBy, reason, callback);
callback.get();
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of(), blockingCallback);
blockingCallback.get();
APIv3.respondJson(ctx, grant);
AuditLog.log(removedBy.getId(), requestBody.getString("removedByIp"), ctx, AuditLogActionType.GRANT_DELETE,ImmutableMap.of("grantId", grant.getId()), (ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, grant);
}
});
}
}

View File

@ -1,9 +1,12 @@
package net.frozenorb.apiv3.route.grants;
import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
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.Rank;
import net.frozenorb.apiv3.model.ServerGroup;
@ -79,7 +82,18 @@ public final class POSTGrants implements Handler<RoutingContext> {
BlockingCallback<Void> callback = new BlockingCallback<>();
grant.insert(callback);
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);
callback.get();
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();
APIv3.respondJson(ctx, ipBan);
}

View File

@ -50,7 +50,7 @@ public final class DELETEPunishmentsId implements Handler<RoutingContext> {
punishment.delete(removedBy, reason, removeCallback);
removeCallback.get();
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();
APIv3.respondJson(ctx, punishment);
}

View File

@ -50,21 +50,29 @@ public final class DELETEUsersIdActivePunishment implements Handler<RoutingConte
BlockingCallback<List<Punishment>> callback = new BlockingCallback<>();
Punishment.findByUserAndType(target, ImmutableSet.of(type), callback);
Punishment activePunishment = null;
for (Punishment punishment : callback.get()) {
if (punishment.isActive()) {
BlockingCallback<UpdateResult> punishmentCallback = new BlockingCallback<>();
punishment.delete(removedBy, reason, punishmentCallback);
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;
activePunishment = punishment;
break;
}
}
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);
}
}