diff --git a/pom.xml b/pom.xml index 8551651..5fd7fa3 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,11 @@ mongo-java-driver 3.2.2 + + com.cribbstechnologies.clients + mandrillClient + 1.1 + org.mongodb.morphia morphia diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index ece63e3..77bf0d7 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -7,22 +7,25 @@ import com.mongodb.MongoClient; import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; import lombok.Getter; -import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.routes.GETDump; +import net.frozenorb.apiv3.routes.GETRoutes; import net.frozenorb.apiv3.routes.announcements.GETAnnouncements; import net.frozenorb.apiv3.routes.chatFilterList.GETChatFilterList; import net.frozenorb.apiv3.routes.grants.DELETEGrant; import net.frozenorb.apiv3.routes.grants.GETGrants; +import net.frozenorb.apiv3.routes.grants.GETUserGrants; import net.frozenorb.apiv3.routes.grants.POSTUserGrant; +import net.frozenorb.apiv3.routes.ipLog.GETUserIPLog; import net.frozenorb.apiv3.routes.punishments.DELETEPunishment; import net.frozenorb.apiv3.routes.punishments.GETPunishment; import net.frozenorb.apiv3.routes.punishments.GETPunishments; import net.frozenorb.apiv3.routes.punishments.POSTUserPunish; +import net.frozenorb.apiv3.routes.ranks.GETRanks; +import net.frozenorb.apiv3.routes.serverGroups.GETServerGroup; +import net.frozenorb.apiv3.routes.serverGroups.GETServerGroups; import net.frozenorb.apiv3.routes.servers.GETServer; 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.GETUsers; +import net.frozenorb.apiv3.routes.users.*; import net.frozenorb.apiv3.weirdStuff.ActorAttributeFilter; import net.frozenorb.apiv3.weirdStuff.ContentTypeFilter; import net.frozenorb.apiv3.weirdStuff.FollowAnnotationExclusionStrategy; @@ -54,10 +57,6 @@ public final class APIv3 { datastore = morphia.createDatastore(mongoClient, "minehqapi"); datastore.ensureIndexes(); - - if (ServerGroup.byId("Website") == null) { - datastore.save(new ServerGroup("Website", "Website")); - } } private void setupHttp() { @@ -70,21 +69,35 @@ public final class APIv3 { delete("/grant/:id", new DELETEGrant(), gson::toJson); get("/grants", new GETGrants(), gson::toJson); + get("/user/:id/grants", new GETUserGrants(), gson::toJson); post("/user/:id:/grant", new POSTUserGrant(), gson::toJson); + get("/user/:id/ipLog", new GETUserIPLog(), gson::toJson); + delete("/punishment/:id", new DELETEPunishment(), gson::toJson); get("/punishment/:id", new GETPunishment(), gson::toJson); get("/punishments", new GETPunishments(), gson::toJson); - post("/user/:id:/punish", new erPunish(), gson::toJson); + post("/user/:id:/punish", new POSTUserPunish(), gson::toJson); + + get("/ranks", new GETRanks(), gson::toJson); + + get("/serverGroup/:id", new GETServerGroup(), gson::toJson); + get("/serverGroups", new GETServerGroups(), gson::toJson); get("/server/:id", new GETServer(), gson::toJson); get("/servers", new GETServers(), gson::toJson); - get("/user/:id", new GETUser(), gson::toJson); - get("/users", new GETUsers(), gson::toJson); get("/staff", new GETStaff(), gson::toJson); + get("/user/:id", new GETUser(), gson::toJson); + get("/user/:id/details", new GETUserDetails(), gson::toJson); + get("/user/:id/meta/:serverGroup", new GETUserMeta(), gson::toJson); + get("/users", new GETUsers(), gson::toJson); + post("/user/:id/notify", new POSTUserNotify(), gson::toJson); + post("/user/:id/register", new POSTUserRegister(), gson::toJson); + put("/user/:id/meta/:serverGroup", new PUTUserMeta(), gson::toJson); get("/dump/:type", new GETDump(), gson::toJson); + get("/routes", new GETRoutes()); after(new ContentTypeFilter()); } diff --git a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java index 11b89c2..4848d40 100644 --- a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java @@ -1,12 +1,14 @@ package net.frozenorb.apiv3.models; import lombok.Getter; +import net.frozenorb.apiv3.APIv3; import org.bson.Document; import org.bson.types.ObjectId; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import java.util.Date; +import java.util.List; import java.util.UUID; @Entity(value = "auditLog", noClassnameStored = true) @@ -20,6 +22,14 @@ public final class AuditLogEntry { @Getter private String description; @Getter private Document actionData; + public static AuditLogEntry byId(String id) { + return APIv3.getDatastore().createQuery(AuditLogEntry.class).field("id").equal(new ObjectId(id)).get(); + } + + public static List values() { + return APIv3.getDatastore().createQuery(AuditLogEntry.class).asList(); + } + public AuditLogEntry() {} // For Morphia public AuditLogEntry(User performedBy, String performedFrom, String actionType, String description, Document actionData) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Grant.java b/src/main/java/net/frozenorb/apiv3/models/Grant.java index 1017616..2945a15 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Grant.java +++ b/src/main/java/net/frozenorb/apiv3/models/Grant.java @@ -7,10 +7,7 @@ import org.bson.types.ObjectId; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; @Entity(value = "grants", noClassnameStored = true) public final class Grant { @@ -33,6 +30,10 @@ public final class Grant { return APIv3.getDatastore().createQuery(Grant.class).field("id").equal(new ObjectId(id)).get(); } + public static List values() { + return APIv3.getDatastore().createQuery(Grant.class).asList(); + } + public Grant() {} // For Morphia public Grant(User target, String reason, Set scopes, Rank rank, Date expiresAt, User addedBy) { diff --git a/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java index 8007a4d..f498c1e 100644 --- a/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java @@ -7,6 +7,7 @@ import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import java.util.Date; +import java.util.List; import java.util.UUID; @Entity(value = "ipLog", noClassnameStored = true) @@ -19,6 +20,14 @@ public final class IPLogEntry { @Getter private Date lastSeen; @Getter private int uses; + public static IPLogEntry byId(String id) { + return APIv3.getDatastore().createQuery(IPLogEntry.class).field("id").equal(new ObjectId(id)).get(); + } + + public static List values() { + return APIv3.getDatastore().createQuery(IPLogEntry.class).asList(); + } + public IPLogEntry() {} // For Morphia public IPLogEntry(User user, String ip) { diff --git a/src/main/java/net/frozenorb/apiv3/models/NotificationLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/NotificationLogEntry.java deleted file mode 100644 index 28370b5..0000000 --- a/src/main/java/net/frozenorb/apiv3/models/NotificationLogEntry.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.frozenorb.apiv3.models; - -import lombok.Getter; -import org.bson.types.ObjectId; -import org.mongodb.morphia.annotations.Entity; -import org.mongodb.morphia.annotations.Id; - -import java.util.Date; -import java.util.UUID; - -@Entity(value = "notificationLog", noClassnameStored = true) -public final class NotificationLogEntry { - - @Getter @Id private ObjectId id; - @Getter private UUID target; - @Getter private Date sentAt; - @Getter private NotificationType type; - @Getter private String title; - @Getter private String body; - - public NotificationLogEntry() {} // For Morphia - - public NotificationLogEntry(User target, NotificationType type, String title, String body) { - this.target = target.getId(); - this.sentAt = new Date(); - this.type = type; - this.title = title; - this.body = body; - } - - public enum NotificationType { - - EMAIL, SMS - - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java index 3245c8d..6154b50 100644 --- a/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java @@ -6,6 +6,7 @@ import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -13,7 +14,7 @@ import java.util.UUID; public final class NotificationTemplate { @Getter @Id private String id; - @Getter private String title; + @Getter private String subject; @Getter private String body; @Getter private Date lastUpdatedAt; @Getter private UUID lastUpdatedBy; @@ -22,17 +23,21 @@ public final class NotificationTemplate { return APIv3.getDatastore().createQuery(NotificationTemplate.class).field("id").equalIgnoreCase(id).get(); } + public static List values() { + return APIv3.getDatastore().createQuery(NotificationTemplate.class).asList(); + } + public NotificationTemplate() {} // For Morphia - public NotificationTemplate(String title, String body, User creator) { - this.title = title; + public NotificationTemplate(String subject, String body, User creator) { + this.subject = subject; this.body = body; this.lastUpdatedAt = new Date(); this.lastUpdatedBy = creator.getId(); } - public void update(String title, String body, User updatedBy) { - this.title = title; + public void update(String subject, String body, User updatedBy) { + this.subject = subject; this.body = body; this.lastUpdatedAt = new Date(); this.lastUpdatedBy = updatedBy.getId(); @@ -44,8 +49,8 @@ public final class NotificationTemplate { APIv3.getDatastore().delete(this); } - public String fillTitle(Map replacements) { - return fill(title, replacements); + public String fillSubject(Map replacements) { + return fill(subject, replacements); } public String fillBody(Map replacements) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Punishment.java b/src/main/java/net/frozenorb/apiv3/models/Punishment.java index 1d5a34a..589c88f 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Punishment.java +++ b/src/main/java/net/frozenorb/apiv3/models/Punishment.java @@ -7,6 +7,7 @@ import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import java.util.Date; +import java.util.List; import java.util.UUID; @Entity(value = "punishments", noClassnameStored = true) @@ -30,6 +31,10 @@ public final class Punishment { return APIv3.getDatastore().createQuery(Punishment.class).field("id").equal(new ObjectId(id)).get(); } + public static List values() { + return APIv3.getDatastore().createQuery(Punishment.class).asList(); + } + public Punishment() {} // For Morphia public Punishment(User target, String reason, PunishmentType type, Date expiresAt, User addedBy, Server addedOn) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Rank.java b/src/main/java/net/frozenorb/apiv3/models/Rank.java index 416c68e..49c5775 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Rank.java +++ b/src/main/java/net/frozenorb/apiv3/models/Rank.java @@ -5,6 +5,8 @@ import net.frozenorb.apiv3.APIv3; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; +import java.util.List; + @Entity(value = "ranks", noClassnameStored = true) public final class Rank { @@ -19,6 +21,10 @@ public final class Rank { return APIv3.getDatastore().createQuery(Rank.class).field("id").equalIgnoreCase(id).get(); } + public static List values() { + return APIv3.getDatastore().createQuery(Rank.class).asList(); + } + public Rank() {} // For Morphia public Rank(String id, int weight, String displayName, String gameColor, String websiteColor, boolean staffRank) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Server.java b/src/main/java/net/frozenorb/apiv3/models/Server.java index 33b089e..4d0fea4 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Server.java +++ b/src/main/java/net/frozenorb/apiv3/models/Server.java @@ -5,10 +5,7 @@ import net.frozenorb.apiv3.APIv3; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; @Entity(value = "servers", noClassnameStored = true) public final class Server { @@ -27,6 +24,10 @@ public final class Server { return APIv3.getDatastore().createQuery(Server.class).field("id").equalIgnoreCase(id).get(); } + public static List values() { + return APIv3.getDatastore().createQuery(Server.class).asList(); + } + public Server() {} // For Morphia public Server(String id, String bungeeId, String displayName, String secret, ServerGroup group, String ip) { diff --git a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java index 6001c41..91497dd 100644 --- a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java @@ -6,6 +6,7 @@ import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import java.util.HashSet; +import java.util.List; import java.util.Set; @Entity(value = "serverGroups", noClassnameStored = true) @@ -20,6 +21,10 @@ public final class ServerGroup { return APIv3.getDatastore().createQuery(ServerGroup.class).field("id").equalIgnoreCase(id).get(); } + public static List values() { + return APIv3.getDatastore().createQuery(ServerGroup.class).asList(); + } + public ServerGroup() {} // For Morphia public ServerGroup(String id, String displayName) { diff --git a/src/main/java/net/frozenorb/apiv3/models/User.java b/src/main/java/net/frozenorb/apiv3/models/User.java index 97594f2..be8deca 100644 --- a/src/main/java/net/frozenorb/apiv3/models/User.java +++ b/src/main/java/net/frozenorb/apiv3/models/User.java @@ -3,13 +3,11 @@ package net.frozenorb.apiv3.models; import lombok.Getter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.weirdStuff.ExcludeFromReplies; +import org.bson.Document; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; @Entity(value = "users", noClassnameStored = true) public final class User { @@ -26,6 +24,14 @@ public final class User { @Getter private Date lastSeenAt; @Getter private Date firstSeen; + public static User byId(String id) { + try { + return byId(UUID.fromString(id)); + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid UUID string " + id, ex); + } + } + public static User byId(UUID id) { return APIv3.getDatastore().createQuery(User.class).field("id").equal(id).get(); } @@ -46,6 +52,10 @@ public final class User { return APIv3.getDatastore().createQuery(User.class).field("lastName").equalIgnoreCase(name).get(); } + public static List values() { + return APIv3.getDatastore().createQuery(User.class).asList(); + } + public User() {} // For Morphia public User(UUID id, String lastName) { @@ -75,4 +85,39 @@ public final class User { return 1 > 0; } + public List getGrants() { + return APIv3.getDatastore().createQuery(Grant.class).field("target").equal(id).asList(); + } + + public List getGrants(ServerGroup scope) { + return APIv3.getDatastore().createQuery(Grant.class).field("target").equal(id).field("scopes").equalIgnoreCase(scope.getId()).asList(); + } + + public List getIPLog() { + return APIv3.getDatastore().createQuery(IPLogEntry.class).field("user").equal(id).asList(); + } + + public List getPunishments() { + return APIv3.getDatastore().createQuery(Punishment.class).field("target").equal(id).asList(); + } + + public List getPunishments(Punishment.PunishmentType type) { + return APIv3.getDatastore().createQuery(Punishment.class).field("target").equal(id).field("type").equal(type).asList(); + } + + public UserMetaEntry getMeta(ServerGroup group) { + return APIv3.getDatastore().createQuery(UserMetaEntry.class).field("user").equal(id).field("serverGroup").equalIgnoreCase(group.getId()).get(); + } + + public void saveMeta(ServerGroup group, Document data) { + UserMetaEntry entry = getMeta(group); + + if (entry == null) { + APIv3.getDatastore().save(new UserMetaEntry(this, group, data)); + } else { + entry.setData(data); + APIv3.getDatastore().save(entry); + } + } + } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java new file mode 100644 index 0000000..cb75528 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java @@ -0,0 +1,42 @@ +package net.frozenorb.apiv3.models; + +import lombok.Getter; +import lombok.Setter; +import net.frozenorb.apiv3.APIv3; +import org.bson.Document; +import org.bson.types.ObjectId; +import org.mongodb.morphia.annotations.Entity; +import org.mongodb.morphia.annotations.Id; + +import java.util.List; +import java.util.UUID; + +@Entity(value = "userMeta", noClassnameStored = true) +public final class UserMetaEntry { + + @Getter @Id private ObjectId id; + @Getter private UUID user; + @Getter private String serverGroup; + @Getter @Setter private Document data; + + public static UserMetaEntry byId(String id) { + return APIv3.getDatastore().createQuery(UserMetaEntry.class).field("id").equal(new ObjectId(id)).get(); + } + + public static List values() { + return APIv3.getDatastore().createQuery(UserMetaEntry.class).asList(); + } + + public UserMetaEntry() {} // For Morphia + + public UserMetaEntry(User user, ServerGroup serverGroup, Document data) { + this.user = user.getId(); + this.serverGroup = serverGroup.getId(); + this.data = data; + } + + public void delete() { + APIv3.getDatastore().delete(this); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/GETRoutes.java b/src/main/java/net/frozenorb/apiv3/routes/GETRoutes.java new file mode 100644 index 0000000..eb8bbc6 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/GETRoutes.java @@ -0,0 +1,71 @@ +package net.frozenorb.apiv3.routes; + +import spark.Request; +import spark.Response; +import spark.Route; +import spark.Spark; +import spark.route.HttpMethod; +import spark.route.SimpleRouteMatcher; + +import java.lang.reflect.Field; +import java.util.List; + +import static spark.Spark.halt; + +public final class GETRoutes implements Route { + + private List routes; + private Field httpMethodField; + private Field pathField; + private Field targetField; + + public GETRoutes() { + try { + Object spark = Spark.getInstance(); + Field routeMatcherField = spark.getClass().getDeclaredField("routeMatcher"); + routeMatcherField.setAccessible(true); + SimpleRouteMatcher routeMatcher = (SimpleRouteMatcher) routeMatcherField.get(spark); + + Field routesField = routeMatcher.getClass().getDeclaredField("routes"); + routesField.setAccessible(true); + routes = (List) routesField.get(routeMatcher); + + Class routeEntryClass = Class.forName("spark.route.RouteEntry"); + + httpMethodField = routeEntryClass.getDeclaredField("httpMethod"); + httpMethodField.setAccessible(true); + pathField = routeEntryClass.getDeclaredField("path"); + pathField.setAccessible(true); + targetField = routeEntryClass.getDeclaredField("target"); + targetField.setAccessible(true); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public Object handle(Request req, Response res) { + try { + String response = ""; + + for (Object route : routes) { + HttpMethod httpMethod = (HttpMethod) httpMethodField.get(route); + String path = (String) pathField.get(route); + Object target = targetField.get(route); + + if (httpMethod == HttpMethod.before || httpMethod == HttpMethod.after) { + continue; + } + + String append = httpMethod.name().toUpperCase() + " " + path + " (" + target + ")
"; + response += append; + } + + halt(response); // We use halt to avoid our content type filter (gson serialization is already avoided because we don't have the filter on this route) + return null; // Return is required but will never be reached + } catch (IllegalAccessException ex) { // If we use a generic Exception we'd catch the halt() call (it throws an exception) + ex.printStackTrace(); + return ex; + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java index 29c0ec7..383e891 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java @@ -10,8 +10,6 @@ import spark.Request; import spark.Response; import spark.Route; -import java.util.UUID; - public final class DELETEGrant implements Route { public Object handle(Request req, Response res) { @@ -23,7 +21,7 @@ public final class DELETEGrant implements Route { return ErrorUtils.error("Cannot remove an inactive grant."); } - User removedBy = User.byId(UUID.fromString(req.queryParams("removedBy"))); + User removedBy = User.byId(req.queryParams("removedBy")); String requiredPermission = Permissions.REMOVE_GRANT + "." + grant.getRank(); if (removedBy == null) { diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java b/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java index 8989036..92e7ac6 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java @@ -1,7 +1,5 @@ package net.frozenorb.apiv3.routes.grants; -import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.weirdStuff.ErrorUtils; import spark.Request; @@ -17,7 +15,7 @@ public final class GETUserGrants implements Route { return ErrorUtils.notFound("User", req.params("id")); } - return APIv3.getDatastore().createQuery(Grant.class).order("addedAt").limit(limit).offset(offset).asList(); + return target.getGrants(); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java index 9bfe926..c5ed15b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java @@ -14,7 +14,6 @@ import spark.Route; import java.util.Date; import java.util.HashSet; import java.util.Set; -import java.util.UUID; public final class POSTUserGrant implements Route { @@ -46,7 +45,7 @@ public final class POSTUserGrant implements Route { return ErrorUtils.invalidInput("Expiration date cannot be in the past."); } - User addedBy = User.byId(UUID.fromString(req.queryParams("addedBy"))); + User addedBy = User.byId(req.queryParams("addedBy")); String requiredPermission = Permissions.CREATE_GRANT + "." + rank.getId(); if (addedBy == null) { diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java b/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java new file mode 100644 index 0000000..96455f3 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java @@ -0,0 +1,21 @@ +package net.frozenorb.apiv3.routes.ipLog; + +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.weirdStuff.ErrorUtils; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class GETUserIPLog implements Route { + + public Object handle(Request req, Response res) { + User target = User.byIdOrName(req.params("id")); + + if (target == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + return target.getIPLog(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java index 48c0055..f9cd66a 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java @@ -10,8 +10,6 @@ import spark.Request; import spark.Response; import spark.Route; -import java.util.UUID; - public final class DELETEPunishment implements Route { public Object handle(Request req, Response res) { @@ -23,7 +21,7 @@ public final class DELETEPunishment implements Route { return ErrorUtils.error("Cannot remove an inactive punishment."); } - User removedBy = User.byId(UUID.fromString(req.queryParams("removedBy"))); + User removedBy = User.byId(req.queryParams("removedBy")); String requiredPermission = Permissions.REMOVE_PUNISHMENT + "." + punishment.getType().name(); if (removedBy == null) { diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java index 4c8a67e..bfcd539 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java @@ -1,7 +1,6 @@ package net.frozenorb.apiv3.routes.punishments; import net.frozenorb.apiv3.models.Punishment; -import net.frozenorb.apiv3.models.Server; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java index 90885dd..efa2a04 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java @@ -11,7 +11,6 @@ import spark.Response; import spark.Route; import java.util.Date; -import java.util.UUID; public final class POSTUserPunish implements Route { @@ -35,7 +34,7 @@ public final class POSTUserPunish implements Route { return ErrorUtils.invalidInput("Expiration date cannot be in the past."); } - User addedBy = User.byId(UUID.fromString(req.queryParams("addedBy"))); + User addedBy = User.byId(req.queryParams("addedBy")); String requiredPermission = Permissions.CREATE_PUNISHMENT + "." + type.name(); if (addedBy == null) { diff --git a/src/main/java/net/frozenorb/apiv3/routes/ranks/GETRanks.java b/src/main/java/net/frozenorb/apiv3/routes/ranks/GETRanks.java new file mode 100644 index 0000000..6d366da --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/ranks/GETRanks.java @@ -0,0 +1,14 @@ +package net.frozenorb.apiv3.routes.ranks; + +import net.frozenorb.apiv3.models.Rank; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class GETRanks implements Route { + + public Object handle(Request req, Response res) { + return Rank.values(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/serverGroups/GETServerGroup.java b/src/main/java/net/frozenorb/apiv3/routes/serverGroups/GETServerGroup.java new file mode 100644 index 0000000..f6ef344 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/serverGroups/GETServerGroup.java @@ -0,0 +1,14 @@ +package net.frozenorb.apiv3.routes.serverGroups; + +import net.frozenorb.apiv3.models.ServerGroup; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class GETServerGroup implements Route { + + public Object handle(Request req, Response res) { + return ServerGroup.byId(req.params("id")); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/serverGroups/GETServerGroups.java b/src/main/java/net/frozenorb/apiv3/routes/serverGroups/GETServerGroups.java new file mode 100644 index 0000000..c330538 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/serverGroups/GETServerGroups.java @@ -0,0 +1,14 @@ +package net.frozenorb.apiv3.routes.serverGroups; + +import net.frozenorb.apiv3.models.ServerGroup; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class GETServerGroups implements Route { + + public Object handle(Request req, Response res) { + return ServerGroup.values(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/servers/GETServers.java b/src/main/java/net/frozenorb/apiv3/routes/servers/GETServers.java index 29f9311..c1a853f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/servers/GETServers.java +++ b/src/main/java/net/frozenorb/apiv3/routes/servers/GETServers.java @@ -1,6 +1,5 @@ package net.frozenorb.apiv3.routes.servers; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.Server; import spark.Request; import spark.Response; @@ -9,7 +8,7 @@ import spark.Route; public final class GETServers implements Route { public Object handle(Request req, Response res) { - return APIv3.getDatastore().createQuery(Server.class).asList(); + return Server.values(); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java new file mode 100644 index 0000000..c7cc54d --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java @@ -0,0 +1,32 @@ +package net.frozenorb.apiv3.routes.users; + +import net.frozenorb.apiv3.models.ServerGroup; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.models.UserMetaEntry; +import net.frozenorb.apiv3.weirdStuff.ErrorUtils; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class DELETEUserMeta implements Route { + + public Object handle(Request req, Response res) { + User user = User.byIdOrName(req.params("id")); + + if (user == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + ServerGroup serverGroup = ServerGroup.byId(req.params("serverGroup")); + + if (serverGroup == null) { + return ErrorUtils.notFound("Server group", req.params("serverGroup")); + } + + UserMetaEntry userMetaEntry = user.getMeta(serverGroup); + + userMetaEntry.delete(); + return userMetaEntry.getData(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java index 997f93f..296e4c0 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java @@ -18,7 +18,7 @@ public final class GETStaff implements Route { public Object handle(Request req, Response res) { Map staffRanks = new HashMap<>(); - APIv3.getDatastore().createQuery(Rank.class).forEach(rank -> { + Rank.values().forEach(rank -> { if (rank.isStaffRank()) { staffRanks.put(rank.getId(), rank); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java new file mode 100644 index 0000000..b99d239 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java @@ -0,0 +1,27 @@ +package net.frozenorb.apiv3.routes.users; + +import com.google.common.collect.ImmutableMap; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.weirdStuff.ErrorUtils; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class GETUserDetails implements Route { + + public Object handle(Request req, Response res) { + User user = User.byIdOrName(req.params("id")); + + if (user == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + return ImmutableMap.of( + "user", user, + "grants", user.getGrants(), + "ipLog", user.getIPLog(), + "punishments", user.getPunishments() + ); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java new file mode 100644 index 0000000..6b3014e --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java @@ -0,0 +1,30 @@ +package net.frozenorb.apiv3.routes.users; + +import net.frozenorb.apiv3.models.ServerGroup; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.models.UserMetaEntry; +import net.frozenorb.apiv3.weirdStuff.ErrorUtils; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class GETUserMeta implements Route { + + public Object handle(Request req, Response res) { + User user = User.byIdOrName(req.params("id")); + + if (user == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + ServerGroup serverGroup = ServerGroup.byId(req.params("serverGroup")); + + if (serverGroup == null) { + return ErrorUtils.notFound("Server group", req.params("serverGroup")); + } + + UserMetaEntry userMetaEntry = user.getMeta(serverGroup); + return userMetaEntry.getData(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUsers.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUsers.java index 3a1d1d3..bddf7eb 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUsers.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUsers.java @@ -1,6 +1,5 @@ package net.frozenorb.apiv3.routes.users; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.User; import spark.Request; import spark.Response; @@ -9,7 +8,7 @@ import spark.Route; public final class GETUsers implements Route { public Object handle(Request req, Response res) { - return APIv3.getDatastore().createQuery(User.class).asList(); + return User.values(); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java new file mode 100644 index 0000000..89c1a70 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java @@ -0,0 +1,55 @@ +package net.frozenorb.apiv3.routes.users; + +import net.frozenorb.apiv3.models.NotificationTemplate; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.weirdStuff.ErrorUtils; +import net.frozenorb.apiv3.weirdStuff.Notification; +import org.bson.Document; +import spark.Request; +import spark.Response; +import spark.Route; + +import java.util.HashMap; +import java.util.Map; + +public final class POSTUserNotify implements Route { + + public Object handle(Request req, Response res) { + User user = User.byIdOrName(req.params("id")); + + if (user == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + if (user.getEmail() == null || user.getEmail().isEmpty()) { + return ErrorUtils.error("User provided does not have email set."); + } + + NotificationTemplate template = NotificationTemplate.byId(req.queryParams("template")); + + if (template == null) { + return ErrorUtils.notFound("Notification template", req.queryParams("template")); + } + + Map subjectReplacements = new HashMap<>(); + Map bodyReplacements = new HashMap<>(); + + req.queryMap("subject").toMap().forEach((key, values) -> { + subjectReplacements.put(key, values[0]); + }); + + req.queryMap("body").toMap().forEach((key, values) -> { + bodyReplacements.put(key, values[0]); + }); + + try { + Notification notification = new Notification(template, subjectReplacements, bodyReplacements); + + notification.sendAsEmail(user.getEmail()); + return new Document("success", true); + } catch (Exception ex) { + return ErrorUtils.error("Failed to send notification"); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java new file mode 100644 index 0000000..831ef58 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java @@ -0,0 +1,25 @@ +package net.frozenorb.apiv3.routes.users; + +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.weirdStuff.ErrorUtils; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class POSTUserRegister implements Route { + + public Object handle(Request req, Response res) { + User user = User.byIdOrName(req.params("id")); + + if (user == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + if (user.getEmail() == null || user.getEmail().isEmpty()) { + return ErrorUtils.error("User provided does not have email set."); + } + + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java new file mode 100644 index 0000000..5774a87 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java @@ -0,0 +1,32 @@ +package net.frozenorb.apiv3.routes.users; + +import net.frozenorb.apiv3.models.ServerGroup; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.weirdStuff.ErrorUtils; +import org.bson.Document; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class PUTUserMeta implements Route { + + public Object handle(Request req, Response res) { + User user = User.byIdOrName(req.params("id")); + + if (user == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + ServerGroup serverGroup = ServerGroup.byId(req.params("serverGroup")); + + if (serverGroup == null) { + return ErrorUtils.notFound("Server group", req.params("serverGroup")); + } + + Document data = Document.parse(req.body()); + + user.saveMeta(serverGroup, data); + return data; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/weirdStuff/ActorAttributeFilter.java b/src/main/java/net/frozenorb/apiv3/weirdStuff/ActorAttributeFilter.java index c7367b0..7b4b09a 100644 --- a/src/main/java/net/frozenorb/apiv3/weirdStuff/ActorAttributeFilter.java +++ b/src/main/java/net/frozenorb/apiv3/weirdStuff/ActorAttributeFilter.java @@ -1,6 +1,5 @@ package net.frozenorb.apiv3.weirdStuff; -import net.frozenorb.apiv3.models.Server; import spark.Filter; import spark.Request; import spark.Response; @@ -8,7 +7,7 @@ import spark.Response; public final class ActorAttributeFilter implements Filter { public void handle(Request req, Response res) { - req.attribute("server", Server.byId(req.queryParams("server"))); + //req.attribute("server", Server.byId(req.queryParams("server"))); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/weirdStuff/MandrillUtils.java b/src/main/java/net/frozenorb/apiv3/weirdStuff/MandrillUtils.java new file mode 100644 index 0000000..543b0b9 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/weirdStuff/MandrillUtils.java @@ -0,0 +1,41 @@ +package net.frozenorb.apiv3.weirdStuff; + +import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest; +import com.cribbstechnologies.clients.mandrill.request.MandrillRESTRequest; +import com.cribbstechnologies.clients.mandrill.util.MandrillConfiguration; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.experimental.UtilityClass; +import org.apache.http.impl.client.DefaultHttpClient; + +import java.util.Properties; + +@UtilityClass +public class MandrillUtils { + + public static MandrillMessagesRequest createMessagesRequest() { + Properties props = new Properties(); + + try { + props.load(MandrillUtils.class.getClassLoader().getResourceAsStream("mandrill.properties")); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + MandrillConfiguration config = new MandrillConfiguration(); + + config.setApiKey(props.getProperty("apiKey")); + config.setApiVersion("1.0"); + config.setBaseURL("https://mandrillapp.com/api"); + + MandrillRESTRequest request = new MandrillRESTRequest(); + + request.setConfig(config); + request.setObjectMapper(new ObjectMapper()); + request.setHttpClient(new DefaultHttpClient()); + + MandrillMessagesRequest messagesRequest = new MandrillMessagesRequest(); + messagesRequest.setRequest(request); + return messagesRequest; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/weirdStuff/Notification.java b/src/main/java/net/frozenorb/apiv3/weirdStuff/Notification.java new file mode 100644 index 0000000..209d7eb --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/weirdStuff/Notification.java @@ -0,0 +1,50 @@ +package net.frozenorb.apiv3.weirdStuff; + +import com.cribbstechnologies.clients.mandrill.exception.RequestFailedException; +import com.cribbstechnologies.clients.mandrill.model.MandrillHtmlMessage; +import com.cribbstechnologies.clients.mandrill.model.MandrillMessageRequest; +import com.cribbstechnologies.clients.mandrill.model.MandrillRecipient; +import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest; +import net.frozenorb.apiv3.models.NotificationTemplate; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import java.io.IOException; +import java.util.Map; + +public final class Notification { + + private static MandrillMessagesRequest messagesRequest = MandrillUtils.createMessagesRequest(); + + private String subject; + private String body; + + public Notification(NotificationTemplate template, Map subjectReplacements, Map bodyReplacements) { + this.subject = template.fillSubject(subjectReplacements); + this.body = template.fillSubject(bodyReplacements); + } + + public void sendAsEmail(String email) throws IOException { + MandrillHtmlMessage message = new MandrillHtmlMessage(); + + message.setFrom_email("no-reply@minehq.com"); + message.setFrom_name("MineHQ Network"); + message.setSubject(subject); + message.setHtml(body); + message.setTo(new MandrillRecipient[] { + new MandrillRecipient(null, email) + }); + + try { + MandrillMessageRequest request = new MandrillMessageRequest(); + request.setMessage(message); + messagesRequest.sendMessage(request); + } catch (RequestFailedException ex) { + throw new IOException("Failed to send notification to user", ex); + } + } + + public void sendAsText(String phoneNumber) throws IOException { + throw new IOException(new NotImplementedException()); + } + +} \ No newline at end of file diff --git a/src/main/resources/mandrill.properties b/src/main/resources/mandrill.properties new file mode 100644 index 0000000..1e9c90e --- /dev/null +++ b/src/main/resources/mandrill.properties @@ -0,0 +1 @@ +apiKey=0OYtwymqJP6oqvszeJu0vQ \ No newline at end of file