Push a bunch of changes

This commit is contained in:
Colin McDonald 2016-04-17 15:23:02 -04:00
parent 922357fc7f
commit fb12a13528
22 changed files with 131 additions and 37 deletions

View File

@ -7,7 +7,7 @@ import com.mongodb.MongoClient;
import com.mongodb.MongoCredential; import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress; import com.mongodb.ServerAddress;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.models.*; import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.routes.announcements.GETAnnouncements; import net.frozenorb.apiv3.routes.announcements.GETAnnouncements;
import net.frozenorb.apiv3.routes.chatFilterList.GETChatFilterList; import net.frozenorb.apiv3.routes.chatFilterList.GETChatFilterList;
import net.frozenorb.apiv3.routes.grants.DELETEGrant; import net.frozenorb.apiv3.routes.grants.DELETEGrant;
@ -18,9 +18,9 @@ import net.frozenorb.apiv3.routes.punishments.GETPunishments;
import net.frozenorb.apiv3.routes.punishments.POSTUserPunish; import net.frozenorb.apiv3.routes.punishments.POSTUserPunish;
import net.frozenorb.apiv3.routes.servers.GETServer; import net.frozenorb.apiv3.routes.servers.GETServer;
import net.frozenorb.apiv3.routes.servers.GETServers; import net.frozenorb.apiv3.routes.servers.GETServers;
import net.frozenorb.apiv3.routes.users.GETStaff;
import net.frozenorb.apiv3.routes.users.GETUser; import net.frozenorb.apiv3.routes.users.GETUser;
import net.frozenorb.apiv3.routes.users.GETUsers; import net.frozenorb.apiv3.routes.users.GETUsers;
import net.frozenorb.apiv3.routes.users.GETStaff;
import net.frozenorb.apiv3.weirdStuff.ActorAttributeFilter; import net.frozenorb.apiv3.weirdStuff.ActorAttributeFilter;
import net.frozenorb.apiv3.weirdStuff.ContentTypeFilter; import net.frozenorb.apiv3.weirdStuff.ContentTypeFilter;
import net.frozenorb.apiv3.weirdStuff.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.weirdStuff.FollowAnnotationExclusionStrategy;
@ -29,8 +29,6 @@ import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia; import org.mongodb.morphia.Morphia;
import java.util.*;
import static spark.Spark.*; import static spark.Spark.*;
public final class APIv3 { public final class APIv3 {
@ -54,6 +52,10 @@ public final class APIv3 {
datastore = morphia.createDatastore(mongoClient, "minehqapi"); datastore = morphia.createDatastore(mongoClient, "minehqapi");
datastore.ensureIndexes(); datastore.ensureIndexes();
if (ServerGroup.byId("Website") == null) {
datastore.save(new ServerGroup("Website", "Website"));
}
} }
private void setupHttp() { private void setupHttp() {
@ -76,7 +78,7 @@ public final class APIv3 {
get("/servers", new GETServers(), gson::toJson); get("/servers", new GETServers(), gson::toJson);
get("/user/:id", new GETUser(), gson::toJson); get("/user/:id", new GETUser(), gson::toJson);
get("/users", new GETUsers() gson::toJson); get("/users", new GETUsers(), gson::toJson);
get("/staff", new GETStaff(), gson::toJson); get("/staff", new GETStaff(), gson::toJson);
after(new ContentTypeFilter()); after(new ContentTypeFilter());

View File

@ -12,24 +12,22 @@ import java.util.UUID;
@Entity(value = "auditLog", noClassnameStored = true) @Entity(value = "auditLog", noClassnameStored = true)
public final class AuditLogEntry { public final class AuditLogEntry {
@Id private ObjectId id; @Getter @Id private ObjectId id;
@Getter private UUID performedBy; @Getter private UUID performedBy;
@Getter private Date performedAt; @Getter private Date performedAt;
@Getter private String performedFrom; @Getter private String performedFrom;
@Getter private String actionType; @Getter private String actionType;
@Getter private String description;
@Getter private Document actionData; @Getter private Document actionData;
public AuditLogEntry() {} // For Morphia public AuditLogEntry() {} // For Morphia
public AuditLogEntry(User performedBy, String performedFromIp, String actionType) { public AuditLogEntry(User performedBy, String performedFrom, String actionType, String description, Document actionData) {
this(performedBy, performedFromIp, actionType, new Document());
}
public AuditLogEntry(User performedBy, String performedFrom, String actionType, Document actionData) {
this.performedBy = performedBy.getId(); this.performedBy = performedBy.getId();
this.performedAt = new Date(); this.performedAt = new Date();
this.performedFrom = performedFrom; this.performedFrom = performedFrom;
this.actionType = actionType; this.actionType = actionType;
this.description = description;
this.actionData = actionData; this.actionData = actionData;
} }

View File

@ -7,12 +7,15 @@ import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Id;
import java.util.*; import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@Entity(value = "grants", noClassnameStored = true) @Entity(value = "grants", noClassnameStored = true)
public final class Grant { public final class Grant {
@Id private ObjectId id; @Getter @Id private ObjectId id;
@Getter private UUID target; @Getter private UUID target;
@Getter private String reason; @Getter private String reason;
@Getter private Set<String> scopes; @Getter private Set<String> scopes;

View File

@ -1,5 +1,6 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import lombok.Getter;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Id;
@ -7,7 +8,7 @@ import org.mongodb.morphia.annotations.Id;
@Entity(value = "ipBans", noClassnameStored = true) @Entity(value = "ipBans", noClassnameStored = true)
public final class IPBan { public final class IPBan {
@Id private ObjectId id; @Getter @Id private ObjectId id;
public IPBan() {} // For Morphia public IPBan() {} // For Morphia

View File

@ -12,7 +12,7 @@ import java.util.UUID;
@Entity(value = "ipLog", noClassnameStored = true) @Entity(value = "ipLog", noClassnameStored = true)
public final class IPLogEntry { public final class IPLogEntry {
@Id private ObjectId id; @Getter @Id private ObjectId id;
@Getter private UUID user; @Getter private UUID user;
@Getter private String ip; @Getter private String ip;
@Getter private Date firstSeen; @Getter private Date firstSeen;

View File

@ -11,7 +11,7 @@ import java.util.UUID;
@Entity(value = "notificationLog", noClassnameStored = true) @Entity(value = "notificationLog", noClassnameStored = true)
public final class NotificationLogEntry { public final class NotificationLogEntry {
@Id private ObjectId id; @Getter @Id private ObjectId id;
@Getter private UUID target; @Getter private UUID target;
@Getter private Date sentAt; @Getter private Date sentAt;
@Getter private NotificationType type; @Getter private NotificationType type;
@ -20,8 +20,8 @@ public final class NotificationLogEntry {
public NotificationLogEntry() {} // For Morphia public NotificationLogEntry() {} // For Morphia
public NotificationLogEntry(UUID target, NotificationType type, String title, String body) { public NotificationLogEntry(User target, NotificationType type, String title, String body) {
this.target = target; this.target = target.getId();
this.sentAt = new Date(); this.sentAt = new Date();
this.type = type; this.type = type;
this.title = title; this.title = title;

View File

@ -12,14 +12,14 @@ import java.util.UUID;
@Entity(value = "notificationTemplates", noClassnameStored = true) @Entity(value = "notificationTemplates", noClassnameStored = true)
public final class NotificationTemplate { public final class NotificationTemplate {
@Id private String id; @Getter @Id private String id;
@Getter private String title; @Getter private String title;
@Getter private String body; @Getter private String body;
@Getter private Date lastUpdatedAt; @Getter private Date lastUpdatedAt;
@Getter private UUID lastUpdatedBy; @Getter private UUID lastUpdatedBy;
public static NotificationTemplate byId(String id) { public static NotificationTemplate byId(String id) {
return APIv3.getDatastore().createQuery(NotificationTemplate.class).field("id").equal(id).get(); return APIv3.getDatastore().createQuery(NotificationTemplate.class).field("id").equalIgnoreCase(id).get();
} }
public NotificationTemplate() {} // For Morphia public NotificationTemplate() {} // For Morphia

View File

@ -12,7 +12,7 @@ import java.util.UUID;
@Entity(value = "punishments", noClassnameStored = true) @Entity(value = "punishments", noClassnameStored = true)
public final class Punishment { public final class Punishment {
@Id private ObjectId id; @Getter @Id private ObjectId id;
@Getter private UUID target; @Getter private UUID target;
@Getter private String reason; @Getter private String reason;
@Getter private PunishmentType type; @Getter private PunishmentType type;
@ -42,8 +42,8 @@ public final class Punishment {
this.addedOn = addedOn.getId(); this.addedOn = addedOn.getId();
} }
public void delete(UUID removedBy, String reason) { public void delete(User removedBy, String reason) {
this.removedBy = removedBy; this.removedBy = removedBy.getId();
this.removedAt = new Date(); this.removedAt = new Date();
this.removalReason = reason; this.removalReason = reason;

View File

@ -16,7 +16,7 @@ public final class Rank {
@Getter private boolean staffRank; @Getter private boolean staffRank;
public static Rank byId(String id) { public static Rank byId(String id) {
return APIv3.getDatastore().createQuery(Rank.class).field("id").equal(id).get(); return APIv3.getDatastore().createQuery(Rank.class).field("id").equalIgnoreCase(id).get();
} }
public Rank() {} // For Morphia public Rank() {} // For Morphia

View File

@ -5,7 +5,10 @@ import net.frozenorb.apiv3.APIv3;
import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Id;
import java.util.*; import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@Entity(value = "servers", noClassnameStored = true) @Entity(value = "servers", noClassnameStored = true)
public final class Server { public final class Server {
@ -21,7 +24,7 @@ public final class Server {
@Getter private Set<UUID> players; @Getter private Set<UUID> players;
public static Server byId(String id) { public static Server byId(String id) {
return APIv3.getDatastore().createQuery(Server.class).field("id").equal(id).get(); return APIv3.getDatastore().createQuery(Server.class).field("id").equalIgnoreCase(id).get();
} }
public Server() {} // For Morphia public Server() {} // For Morphia

View File

@ -17,7 +17,7 @@ public final class ServerGroup {
@Getter private Set<String> chatFilterList; @Getter private Set<String> chatFilterList;
public static ServerGroup byId(String id) { public static ServerGroup byId(String id) {
return APIv3.getDatastore().createQuery(ServerGroup.class).field("id").equal(id).get(); return APIv3.getDatastore().createQuery(ServerGroup.class).field("id").equalIgnoreCase(id).get();
} }
public ServerGroup() {} // For Morphia public ServerGroup() {} // For Morphia

View File

@ -40,7 +40,7 @@ public final class User {
@Deprecated @Deprecated
public static User byName(String name) { public static User byName(String name) {
return APIv3.getDatastore().createQuery(User.class).field("lastName").equal(name).get(); return APIv3.getDatastore().createQuery(User.class).field("lastName").equalIgnoreCase(name).get();
} }
public User() {} // For Morphia public User() {} // For Morphia
@ -61,4 +61,15 @@ public final class User {
aliases.put(lastName, new Date()); aliases.put(lastName, new Date());
} }
public boolean hasPermissionAnywhere(String permission) {
return hasPermissionScoped(permission, null);
}
public boolean hasPermissionScoped(String permission, ServerGroup scope) {
// TODO: BLAH FIX THIS IF WE DONT REMOVE THEN IDK WHAT TO SAY
// Also this is 1 > 0 because 'return true;' means all usages of this
// get marked as a warning until we change it.
return 1 > 0;
}
} }

View File

@ -1,9 +1,11 @@
package net.frozenorb.apiv3.routes.grants; package net.frozenorb.apiv3.routes.grants;
import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.weirdStuff.AuditLog;
import net.frozenorb.apiv3.weirdStuff.ErrorUtils;
import net.frozenorb.apiv3.weirdStuff.Permissions;
import org.bson.Document;
import spark.Request; import spark.Request;
import spark.Response; import spark.Response;
import spark.Route; import spark.Route;
@ -14,10 +16,27 @@ public final class DELETEGrant implements Route {
public Object handle(Request req, Response res) { public Object handle(Request req, Response res) {
Grant grant = Grant.byId(req.params("id")); Grant grant = Grant.byId(req.params("id"));
if (grant == null) {
return ErrorUtils.notFound("Grant", req.params("id"));
} else if (!grant.isActive()) {
return ErrorUtils.error("Cannot remove an inactive grant.");
}
User removedBy = User.byId(UUID.fromString(req.queryParams("removedBy"))); User removedBy = User.byId(UUID.fromString(req.queryParams("removedBy")));
String requiredPermission = Permissions.REMOVE_GRANT + "." + grant.getRank();
if (removedBy == null) {
return ErrorUtils.notFound("User", req.queryParams("removedBy"));
} else if (!removedBy.hasPermissionAnywhere(requiredPermission)) {
return ErrorUtils.unauthorized(requiredPermission);
}
String removedByIp = req.queryParams("removedByIp");
String reason = req.queryParams("removalReason"); String reason = req.queryParams("removalReason");
grant.delete(removedBy.getId(), reason); grant.delete(removedBy, reason);
AuditLog.log(removedBy, removedByIp, "grant.remove", "Removed a " + grant.getRank() + " grant from " + grant.getTarget() + " for \" " + reason + "\"", new Document("grantId", grant.getId()));
return grant; return grant;
} }

View File

@ -2,8 +2,6 @@ package net.frozenorb.apiv3.routes.grants;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.ServerGroup;
import spark.Request; import spark.Request;
import spark.Response; import spark.Response;
import spark.Route; import spark.Route;

View File

@ -18,9 +18,9 @@ public final class POSTUserGrant implements Route {
Set<String> scopes = new HashSet<>(Arrays.asList(req.queryParams("scopes").split(","))); Set<String> scopes = new HashSet<>(Arrays.asList(req.queryParams("scopes").split(",")));
Rank rank = Rank.byId(req.queryParams("rank")); Rank rank = Rank.byId(req.queryParams("rank"));
Date expiresAt = new Date(Long.parseLong(req.queryParams("expiresAt"))); Date expiresAt = new Date(Long.parseLong(req.queryParams("expiresAt")));
User createdBy = User.byId(UUID.fromString(req.queryParams("createdBy"))); User addedBy = User.byId(UUID.fromString(req.queryParams("addedBy")));
Grant grant = new Grant(target.getId(), reason, scopes, rank.getId(), expiresAt, createdBy.getId()); Grant grant = new Grant(target, reason, scopes, rank, expiresAt, addedBy);
APIv3.getDatastore().save(grant); APIv3.getDatastore().save(grant);
return grant; return grant;
} }

View File

@ -2,6 +2,8 @@ package net.frozenorb.apiv3.routes.punishments;
import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.weirdStuff.AuditLog;
import org.bson.Document;
import spark.Request; import spark.Request;
import spark.Response; import spark.Response;
import spark.Route; import spark.Route;
@ -13,9 +15,11 @@ public final class DELETEPunishment implements Route {
public Object handle(Request req, Response res) { public Object handle(Request req, Response res) {
Punishment punishment = Punishment.byId(req.params("id")); Punishment punishment = Punishment.byId(req.params("id"));
User removedBy = User.byId(UUID.fromString(req.queryParams("removedBy"))); User removedBy = User.byId(UUID.fromString(req.queryParams("removedBy")));
String removedByIp = req.queryParams("removedByIp");
String reason = req.queryParams("removalReason"); String reason = req.queryParams("removalReason");
punishment.delete(removedBy.getId(), reason); punishment.delete(removedBy, reason);
AuditLog.log(removedBy, removedByIp, "punishment.remove", "Removed a " + punishment.getType().name().toLowerCase() + " from " + punishment.getTarget() + " for \" " + reason + "\"", new Document("punishmentId", punishment.getId()));
return punishment; return punishment;
} }

View File

@ -21,7 +21,7 @@ public final class POSTUserPunish implements Route {
User createdBy = User.byId(UUID.fromString(req.queryParams("createdBy"))); User createdBy = User.byId(UUID.fromString(req.queryParams("createdBy")));
Server addedOn = Server.byId(req.queryParams("addedOn")); Server addedOn = Server.byId(req.queryParams("addedOn"));
Punishment punishment = new Punishment(target.getId(), reason, type, expiresAt, createdBy.getId(), addedOn.getId()); Punishment punishment = new Punishment(target, reason, type, expiresAt, createdBy, addedOn);
APIv3.getDatastore().save(punishment); APIv3.getDatastore().save(punishment);
return punishment; return punishment;
} }

View File

@ -1,5 +1,6 @@
package net.frozenorb.apiv3.routes.servers; package net.frozenorb.apiv3.routes.servers;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
import spark.Request; import spark.Request;
import spark.Response; import spark.Response;

View File

@ -0,0 +1,20 @@
package net.frozenorb.apiv3.weirdStuff;
import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.AuditLogEntry;
import net.frozenorb.apiv3.models.User;
import org.bson.Document;
@UtilityClass
public class AuditLog {
public static void log(User performedBy, String performedFrom, String actionType, String description) {
log(performedBy, performedFrom, actionType, description, new Document());
}
public static void log(User performedBy, String performedFrom, String actionType, String description, Document actionData) {
APIv3.getDatastore().save(new AuditLogEntry(performedBy, performedFrom, actionType, description, actionData));
}
}

View File

@ -0,0 +1,21 @@
package net.frozenorb.apiv3.weirdStuff;
import lombok.experimental.UtilityClass;
import org.bson.Document;
@UtilityClass
public class ErrorUtils {
public static Document notFound(String itemType, String id) {
return error(itemType + " with id " + id + " cannot be found.");
}
public static Document unauthorized(String permission) {
return error("Unauthorized access. Permission \"" + permission + "\" required.");
}
public static Document error(String reason) {
return new Document("success", false).append("reason", reason);
}
}

View File

@ -13,6 +13,9 @@ public final class ObjectIdTypeAdapter extends TypeAdapter<ObjectId> {
writer.value(write.toString()); writer.value(write.toString());
} }
// This is used with Gson, which is only used
// to serialize outgoing responses, thus we
// don't need to have a read method.
public ObjectId read(JsonReader reader) { public ObjectId read(JsonReader reader) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }

View File

@ -0,0 +1,10 @@
package net.frozenorb.apiv3.weirdStuff;
import lombok.experimental.UtilityClass;
@UtilityClass
public class Permissions {
public static final String REMOVE_GRANT = "minehq.grant.remove"; // minehq.grant.remove.%RANK%
}