From 583f91b45ee7eee032e16920a22bc64fbedb26b1 Mon Sep 17 00:00:00 2001 From: Colin McDonald Date: Sat, 30 Apr 2016 20:08:58 -0400 Subject: [PATCH] Whoo more permission stuff --- .../apiv3.properties => apiv3.properties | 5 +- src/main/java/net/frozenorb/apiv3/APIv3.java | 57 +++++++-------- src/main/java/net/frozenorb/apiv3/Main.java | 2 +- .../apiv3/{unsorted => actors}/Actor.java | 2 +- .../frozenorb/apiv3/actors/ServerActor.java | 25 +++++++ .../frozenorb/apiv3/actors/UnknownActor.java | 17 +++++ .../net/frozenorb/apiv3/actors/UserActor.java | 39 ++++++++++ .../frozenorb/apiv3/actors/WebsiteActor.java | 17 +++++ .../apiv3/filters/ActorAttributeFilter.java | 73 +++++++++++++++---- .../apiv3/filters/AuthorizationFilter.java | 4 +- .../frozenorb/apiv3/models/AuditLogEntry.java | 4 +- .../net/frozenorb/apiv3/models/Grant.java | 6 +- .../frozenorb/apiv3/models/Punishment.java | 2 +- .../java/net/frozenorb/apiv3/models/Rank.java | 2 +- .../net/frozenorb/apiv3/models/Server.java | 10 +-- .../frozenorb/apiv3/models/ServerGroup.java | 34 +++------ .../java/net/frozenorb/apiv3/models/User.java | 71 +++++++++--------- .../frozenorb/apiv3/models/UserMetaEntry.java | 2 +- .../net/frozenorb/apiv3/routes/GETRoutes.java | 72 ------------------ .../announcements/GETAnnouncements.java | 2 +- .../chatFilterList/GETChatFilterList.java | 2 +- .../apiv3/routes/grants/DELETEGrant.java | 4 +- .../apiv3/routes/grants/GETUserGrants.java | 2 +- .../apiv3/routes/grants/POSTUserGrant.java | 15 +++- .../apiv3/routes/ipLog/GETUserIPLog.java | 2 +- .../routes/punishments/DELETEPunishment.java | 4 +- .../routes/punishments/POSTUserPunish.java | 2 +- .../POSTServerHeartbeat.java} | 17 +++-- .../apiv3/routes/users/DELETEUserMeta.java | 2 +- .../frozenorb/apiv3/routes/users/GETUser.java | 2 +- .../apiv3/routes/users/GETUserDetails.java | 2 +- .../apiv3/routes/users/GETUserMeta.java | 2 +- .../POSTUserConfirmRegister.java} | 6 +- .../apiv3/routes/users/POSTUserLoginInfo.java | 9 ++- .../apiv3/routes/users/POSTUserNotify.java | 3 +- .../apiv3/routes/users/POSTUserRegister.java | 9 ++- .../apiv3/routes/users/PUTUserMeta.java | 2 +- .../frozenorb/apiv3/unsorted/AuditLog.java | 1 + .../unsorted/LoggingExceptionHandler.java | 20 +++++ .../apiv3/unsorted/Notification.java | 8 +- .../apiv3/utils/PermissionUtils.java | 50 +++++++++++-- .../net/frozenorb/apiv3/utils/TimeUtils.java | 7 -- 42 files changed, 362 insertions(+), 255 deletions(-) rename src/main/resources/apiv3.properties => apiv3.properties (52%) rename src/main/java/net/frozenorb/apiv3/{unsorted => actors}/Actor.java (81%) create mode 100644 src/main/java/net/frozenorb/apiv3/actors/ServerActor.java create mode 100644 src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java create mode 100644 src/main/java/net/frozenorb/apiv3/actors/UserActor.java create mode 100644 src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java delete mode 100644 src/main/java/net/frozenorb/apiv3/routes/GETRoutes.java rename src/main/java/net/frozenorb/apiv3/routes/{POSTHeartbeat.java => servers/POSTServerHeartbeat.java} (82%) rename src/main/java/net/frozenorb/apiv3/routes/{POSTConfirmRegister.java => users/POSTUserConfirmRegister.java} (89%) create mode 100644 src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java diff --git a/src/main/resources/apiv3.properties b/apiv3.properties similarity index 52% rename from src/main/resources/apiv3.properties rename to apiv3.properties index 1afafda..c81b85d 100644 --- a/src/main/resources/apiv3.properties +++ b/apiv3.properties @@ -5,4 +5,7 @@ mongo.username=test mongo.password=test http.address= http.port=80 -mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ \ No newline at end of file +http.workerThreads=4 +mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ +auth.permittedUserRanks=developer,owner +auth.websiteApiKey=RVbp4hY6sCFVaf \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index 0a8976b..ad54d96 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -11,9 +11,6 @@ import net.frozenorb.apiv3.filters.ActorAttributeFilter; import net.frozenorb.apiv3.filters.AuthorizationFilter; import net.frozenorb.apiv3.filters.ContentTypeFilter; import net.frozenorb.apiv3.routes.GETDump; -import net.frozenorb.apiv3.routes.GETRoutes; -import net.frozenorb.apiv3.routes.POSTConfirmRegister; -import net.frozenorb.apiv3.routes.POSTHeartbeat; import net.frozenorb.apiv3.routes.announcements.GETAnnouncements; import net.frozenorb.apiv3.routes.chatFilterList.GETChatFilterList; import net.frozenorb.apiv3.routes.grants.DELETEGrant; @@ -30,21 +27,23 @@ 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.servers.POSTServerHeartbeat; import net.frozenorb.apiv3.routes.users.*; import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter; +import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler; import org.bson.types.ObjectId; import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Morphia; +import java.io.FileInputStream; +import java.io.InputStream; import java.util.Properties; import static spark.Spark.*; public final class APIv3 { - // TODO: Cleanup main class - @Getter private static Datastore datastore; @Getter private static Properties config = new Properties(); private final Gson gson = new GsonBuilder() @@ -53,80 +52,74 @@ public final class APIv3 { .create(); APIv3() { - try { - // TODO: Load - config.load(APIv3.class.getClassLoader().getResourceAsStream("apiv3.properties")); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - + setupConfig(); setupDatabase(); setupHttp(); } + private void setupConfig() { + try (InputStream in = new FileInputStream("apiv3.properties")) { + config.load(in); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + private void setupDatabase() { MongoClient mongoClient = new MongoClient(new ServerAddress( - (String) config.get("mongo.address"), - (Integer) config.get("mongo.port")), + config.getProperty("mongo.address"), + Integer.parseInt(config.getProperty("mongo.port"))), ImmutableList.of( MongoCredential.createCredential( - (String) config.get("mongo.username"), - (String) config.get("mongo.database"), - ((String) config.get("mongo.password")).toCharArray()) + config.getProperty("mongo.username"), + config.getProperty("mongo.database"), + config.getProperty("mongo.password").toCharArray()) )); Morphia morphia = new Morphia(); morphia.mapPackage("net.frozenorb.apiv3.accessor"); - datastore = morphia.createDatastore(mongoClient, (String) config.get("mongo.database")); + datastore = morphia.createDatastore(mongoClient, config.getProperty("mongo.database")); datastore.ensureIndexes(); } private void setupHttp() { - ipAddress((String) config.get("http.address")); - port((Integer) config.get("http.port")); + ipAddress(config.getProperty("http.address")); + port(Integer.parseInt(config.getProperty("http.port"))); + threadPool(Integer.parseInt(config.getProperty("http.workerThreads"))); before(new ContentTypeFilter()); before(new ActorAttributeFilter()); before(new AuthorizationFilter()); + exception(Exception.class, new LoggingExceptionHandler()); get("/announcements", new GETAnnouncements(), gson::toJson); - get("/chatFilterList", new GETChatFilterList(), gson::toJson); - 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 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); - + post("/server/heartbeat", new POSTServerHeartbeat(), gson::toJson); delete("/user/:id/meta", new DELETEUserMeta(), 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); + post("/user/confirmRegister/:emailToken", new POSTUserConfirmRegister(), gson::toJson); post("/user/:id/loginInfo", new POSTUserLoginInfo(), 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()); - post("/confirmRegister", new POSTConfirmRegister(), gson::toJson); - post("/heartbeat", new POSTHeartbeat(), gson::toJson); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/Main.java b/src/main/java/net/frozenorb/apiv3/Main.java index c52414d..9239ac2 100644 --- a/src/main/java/net/frozenorb/apiv3/Main.java +++ b/src/main/java/net/frozenorb/apiv3/Main.java @@ -1,6 +1,6 @@ package net.frozenorb.apiv3; -public final class Main { +final class Main { public static void main(String[] args) { new APIv3(); diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Actor.java b/src/main/java/net/frozenorb/apiv3/actors/Actor.java similarity index 81% rename from src/main/java/net/frozenorb/apiv3/unsorted/Actor.java rename to src/main/java/net/frozenorb/apiv3/actors/Actor.java index d58e8aa..4fdb98b 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Actor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/Actor.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.unsorted; +package net.frozenorb.apiv3.actors; public interface Actor { diff --git a/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java new file mode 100644 index 0000000..b03cd64 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java @@ -0,0 +1,25 @@ +package net.frozenorb.apiv3.actors; + +import net.frozenorb.apiv3.models.Server; + +public final class ServerActor implements Actor { + + private final Server server; + + public ServerActor(Server server) { + this.server = server; + } + + public boolean isAuthorized() { + return true; + } + + public String getName() { + return server.getId(); + } + + public Actor.Type getType() { + return Actor.Type.SERVER; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java b/src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java new file mode 100644 index 0000000..154c402 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java @@ -0,0 +1,17 @@ +package net.frozenorb.apiv3.actors; + +public final class UnknownActor implements Actor { + + public boolean isAuthorized() { + return false; + } + + public String getName() { + return "Unknown"; + } + + public Actor.Type getType() { + return Actor.Type.UNKNOWN; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/actors/UserActor.java b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java new file mode 100644 index 0000000..7652ee7 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java @@ -0,0 +1,39 @@ +package net.frozenorb.apiv3.actors; + +import com.google.common.collect.ImmutableSet; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.models.User; + +import java.util.Set; + +public final class UserActor implements Actor { + + private static final Set authorizedUserGrants = ImmutableSet.copyOf(APIv3.getConfig().getProperty("auth.permittedUserRanks").split(",")); + + private final User user; + // We use Boolean here so we can have null = not calculated; + private Boolean cachedAuthorized = null; + + public UserActor(User user) { + this.user = user; + } + + public boolean isAuthorized() { + if (cachedAuthorized != null) { + return cachedAuthorized; + } else { + String highestRankId = user.getHighestRank().getId(); + cachedAuthorized = authorizedUserGrants.contains(highestRankId); + return cachedAuthorized; + } + } + + public String getName() { + return user.getLastUsername(); + } + + public Actor.Type getType() { + return Actor.Type.USER; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java b/src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java new file mode 100644 index 0000000..e87ad9c --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java @@ -0,0 +1,17 @@ +package net.frozenorb.apiv3.actors; + +public final class WebsiteActor implements Actor { + + public boolean isAuthorized() { + return true; + } + + public String getName() { + return "Website"; + } + + public Actor.Type getType() { + return Actor.Type.WEBSITE; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java b/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java index 60d627b..d0cfb4a 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java @@ -1,33 +1,74 @@ package net.frozenorb.apiv3.filters; -import net.frozenorb.apiv3.unsorted.Actor; +import com.sun.xml.internal.messaging.saaj.util.Base64; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.actors.*; +import net.frozenorb.apiv3.models.Server; +import net.frozenorb.apiv3.models.User; import spark.Filter; import spark.Request; import spark.Response; +import static spark.Spark.halt; + public final class ActorAttributeFilter implements Filter { public void handle(Request req, Response res) { - // TODO: IF THEYRE A SERVER PERFORM PRE-VALIDATION TO MAKE SURE IT EXISTS - // TODO: Auth! - req.attribute("actor", new Actor() { + String authHeader = req.headers("Authorization"); + String mhqAuthHeader = req.headers("MHQ-Authorization"); - @Override - public boolean isAuthorized() { - return true; + if (authHeader != null) { + req.attribute("actor", processBasicAuthorization(authHeader)); + } else if (mhqAuthHeader != null) { + req.attribute("actor", processMHQAuthorization(mhqAuthHeader)); + } else { + req.attribute("actor", new UnknownActor()); + } + } + + private Actor processBasicAuthorization(String authHeader) { + String encodedHeader = authHeader.substring("Basic ".length()); + String[] credentials = Base64.base64Decode(encodedHeader).split(":"); + + if (credentials.length == 2) { + User user = User.byLastUsername(credentials[0]); + char[] password = credentials[1].toCharArray(); + + if (user != null && user.checkPassword(password)) { + return new UserActor(user); } + } - @Override - public String getName() { - return "HCTeams"; + halt(401); + return null; + } + + private Actor processMHQAuthorization(String authHeader) { + String[] split = authHeader.split(" "); + + if (split.length >= 2) { + String type = split[0]; + + if (type.equals("Website") && split.length == 2) { + String givenKey = split[1]; + String properKey = APIv3.getConfig().getProperty("auth.websiteApiKey"); + + if (givenKey.equals(properKey)) { + return new WebsiteActor(); + } + } else if (type.equals("Server") && split.length == 3) { + Server server = Server.byId(split[1]); + String givenKey = split[2]; + String properKey = server.getApiKey(); + + if (givenKey.equals(properKey)) { + return new ServerActor(server); + } } + } - @Override - public Actor.Type getType() { - return Actor.Type.SERVER; - } - - }); + halt(401); + return null; } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java b/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java index 8c7dace..c02edc4 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java @@ -1,6 +1,6 @@ package net.frozenorb.apiv3.filters; -import net.frozenorb.apiv3.unsorted.Actor; +import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.utils.ErrorUtils; import spark.Filter; import spark.Request; @@ -10,7 +10,7 @@ import spark.Spark; public final class AuthorizationFilter implements Filter { public void handle(Request req, Response res) { - Actor actor = req.attribute("actor"); + Actor actor = req.attribute("actors"); if (!actor.isAuthorized()) { Spark.halt(ErrorUtils.error("Unauthorized access: Please authenticate as either a server, the website, or an authorized user.").toJson()); diff --git a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java index 4221797..bb68b6f 100644 --- a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java @@ -1,7 +1,7 @@ package net.frozenorb.apiv3.models; import lombok.Getter; -import net.frozenorb.apiv3.unsorted.Actor; +import net.frozenorb.apiv3.actors.Actor; import org.bson.Document; import org.bson.types.ObjectId; import org.mongodb.morphia.annotations.Entity; @@ -27,7 +27,7 @@ public final class AuditLogEntry { this.performedAt = new Date(); this.actorName = actor.getName(); this.actionType = actionType; - this.actionData = actionData; + this.actionData = new Document(actionData); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/Grant.java b/src/main/java/net/frozenorb/apiv3/models/Grant.java index 7862575..ef97e60 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Grant.java +++ b/src/main/java/net/frozenorb/apiv3/models/Grant.java @@ -40,7 +40,7 @@ public final class Grant { this.reason = reason; this.scopes = new HashSet<>(Collections2.transform(scopes, ServerGroup::getId)); this.rank = rank.getId(); - this.expiresAt = expiresAt; + this.expiresAt = (Date) expiresAt.clone(); this.addedBy = addedBy.getId(); this.addedAt = new Date(); } @@ -69,10 +69,6 @@ public final class Grant { return removedBy != null; } - public boolean appliesOn(Server server) { - return isGlobal() || scopes.contains(server.getId()); - } - public boolean appliesOn(ServerGroup serverGroup) { return isGlobal() || scopes.contains(serverGroup.getId()); } diff --git a/src/main/java/net/frozenorb/apiv3/models/Punishment.java b/src/main/java/net/frozenorb/apiv3/models/Punishment.java index 1d5a34a..0e3351c 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Punishment.java +++ b/src/main/java/net/frozenorb/apiv3/models/Punishment.java @@ -36,7 +36,7 @@ public final class Punishment { this.target = target.getId(); this.reason = reason; this.type = type; - this.expiresAt = expiresAt; + this.expiresAt = (Date) expiresAt.clone(); this.addedBy = addedBy.getId(); this.addedAt = new Date(); this.addedOn = addedOn.getId(); diff --git a/src/main/java/net/frozenorb/apiv3/models/Rank.java b/src/main/java/net/frozenorb/apiv3/models/Rank.java index 49c5775..6a91639 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Rank.java +++ b/src/main/java/net/frozenorb/apiv3/models/Rank.java @@ -22,7 +22,7 @@ public final class Rank { } public static List values() { - return APIv3.getDatastore().createQuery(Rank.class).asList(); + return APIv3.getDatastore().createQuery(Rank.class).order("weight").asList(); } public Rank() {} // For Morphia diff --git a/src/main/java/net/frozenorb/apiv3/models/Server.java b/src/main/java/net/frozenorb/apiv3/models/Server.java index 3ee169b..03cd0d8 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Server.java +++ b/src/main/java/net/frozenorb/apiv3/models/Server.java @@ -14,7 +14,7 @@ public final class Server { @Getter @Id private String id; @Getter private String bungeeId; @Getter private String displayName; - @Getter private String secret; + @Getter private String apiKey; @Getter private String group; @Getter private String ip; @Getter @Setter private Date lastUpdate; @@ -31,11 +31,11 @@ public final class Server { public Server() {} // For Morphia - public Server(String id, String bungeeId, String displayName, String secret, ServerGroup group, String ip) { + public Server(String id, String bungeeId, String displayName, String apiKey, ServerGroup group, String ip) { this.id = id; this.bungeeId = bungeeId; this.displayName = displayName; - this.secret = secret; + this.apiKey = apiKey; this.group = group.getId(); this.ip = ip; this.lastUpdate = new Date(); @@ -43,8 +43,4 @@ public final class Server { this.players = new HashSet<>(); } - public ServerGroup resolveGroup() { - return ServerGroup.byId(group); - } - } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java index c06b328..bb0cb93 100644 --- a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java @@ -1,7 +1,9 @@ package net.frozenorb.apiv3.models; +import com.google.common.collect.ImmutableSet; import lombok.Getter; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.utils.PermissionUtils; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; @@ -14,6 +16,7 @@ public final class ServerGroup { @Getter private String displayName; @Getter private Set announcements; @Getter private Set chatFilterList; + @Getter private Map> permissions; public static ServerGroup byId(String id) { return APIv3.getDatastore().createQuery(ServerGroup.class).field("id").equalIgnoreCase(id).get(); @@ -30,41 +33,24 @@ public final class ServerGroup { this.displayName = displayName; this.announcements = new HashSet<>(); this.chatFilterList = new HashSet<>(); + this.permissions = new HashMap<>(); } public void setAnnouncements(Set announcements) { - this.announcements = announcements; + this.announcements = ImmutableSet.copyOf(announcements); APIv3.getDatastore().save(this); } public void setChatFilterList(Set chatFilterList) { - this.chatFilterList = chatFilterList; + this.chatFilterList = ImmutableSet.copyOf(chatFilterList); APIv3.getDatastore().save(this); } - // TODO: DO THIS - public Map getPermissions(Rank rank) { - return new HashMap<>(); - } - public Map calculatePermissions(Rank userRank) { - /*ServerGroup scope = ServerGroup.byId("HCTeams"); - Map userRanks = new HashMap<>(); - - - //ServerGroup global = ServerGroup.byId("Global"); - Map calculatedPermissions = global.getPermissions(userRanks.get(global)); - - // for global calculations - - for (ServerGroup serverGroup : ServerGroup.values()) { - mergePermissions(serverGroup, userRanks.get(serverGroup), calculatedPermissions); - } - - // for scoped calculations - - mergePermissions(scope, userRanks.get(scope), calculatedPermissions);*/ - return null; + return PermissionUtils.mergePermissions( + PermissionUtils.getDefaultPermissions(userRank), + PermissionUtils.mergeUpTo(permissions, userRank) + ); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/User.java b/src/main/java/net/frozenorb/apiv3/models/User.java index e8110b3..664516b 100644 --- a/src/main/java/net/frozenorb/apiv3/models/User.java +++ b/src/main/java/net/frozenorb/apiv3/models/User.java @@ -18,7 +18,7 @@ import java.util.*; public final class User { @Getter @Id private UUID id; - @Getter private String lastName; + @Getter private String lastUsername; @Getter @ExcludeFromReplies private Map aliases; @Getter @ExcludeFromReplies private String otpCode; @Getter @ExcludeFromReplies @Setter private String emailToken; @@ -42,31 +42,20 @@ public final class User { return APIv3.getDatastore().createQuery(User.class).field("id").equal(id).get(); } - // TODO: FIND ALL USAGES OF THIS AND - // SEE HOW MANY OF THEM (EX NON GET REQUESTS) - // WE CAN DROP - public static User byIdOrName(String idOrName) { - if (idOrName.length() == 36) { - return byId(UUID.fromString(idOrName)); - } else { - return byName(idOrName); - } - } - - @Deprecated - public static User byName(String name) { - return APIv3.getDatastore().createQuery(User.class).field("lastName").equalIgnoreCase(name).get(); - } - public static User byEmailToken(String name) { return APIv3.getDatastore().createQuery(User.class).field("emailToken").equal(name).get(); } + @Deprecated + public static User byLastUsername(String lastUsername) { + return APIv3.getDatastore().createQuery(User.class).field("lastUsername").equal(lastUsername).get(); + } + public User() {} // For Morphia - public User(UUID id, String lastName) { + public User(UUID id, String lastUsername) { this.id = id; - this.lastName = lastName; + this.lastUsername = lastUsername; this.aliases = new HashMap<>(); this.otpCode = null; this.password = null; @@ -76,18 +65,18 @@ public final class User { this.lastSeenAt = new Date(); this.firstSeen = new Date(); - aliases.put(lastName, new Date()); - } - - public boolean hasPermissionAnywhere(String permission) { - return hasPermissionScoped(permission, null); + aliases.put(lastUsername, new Date()); } 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; + Map permissions = scope.calculatePermissions(getHighestRank(scope)); + return permissions.containsKey(permission) && permissions.get(permission); + } + + // TODO + public boolean hasPermissionAnywhere(String permission) { + Map permissions = /*scope.calculatePermissions(getHighestRank(scope));*/ ImmutableMap.of(); + return permissions.containsKey(permission) && permissions.get(permission); } public List getGrants() { @@ -98,6 +87,17 @@ public final class User { return APIv3.getDatastore().createQuery(IPLogEntry.class).field("user").equal(id).asList(); } + public IPLogEntry getIPLogEntry(String ip) { + IPLogEntry existing = APIv3.getDatastore().createQuery(IPLogEntry.class).field("user").equal(id).field("ip").equal(ip).get(); + + if (existing == null) { + existing = new IPLogEntry(this, ip); + APIv3.getDatastore().save(existing); + } + + return existing; + } + public List getPunishments() { return APIv3.getDatastore().createQuery(Punishment.class).field("target").equal(id).asList(); } @@ -121,6 +121,11 @@ public final class User { } } + public void seenOnServer(Server server) { + this.lastSeenOn = server.getId(); + this.lastSeenAt = new Date(); + } + public void setPassword(char[] unencrypted) { this.password = BCrypt.hashpw(new String(unencrypted), BCrypt.gensalt()); } @@ -133,11 +138,7 @@ public final class User { Rank highest = null; for (Grant grant : getGrants()) { - if (!grant.isActive()) { - continue; - } - - if (serverGroup != null && !grant.appliesOn(serverGroup)) { + if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) { continue; } @@ -155,7 +156,7 @@ public final class User { } } - public Rank getHighestRankAnywhere() { + public Rank getHighestRank() { return getHighestRank(null); } @@ -184,7 +185,7 @@ public final class User { } - ServerGroup actorGroup = server.resolveGroup(); + ServerGroup actorGroup = ServerGroup.byId(server.getGroup()); Rank rank = getHighestRank(actorGroup); Map rankPermissions = actorGroup.calculatePermissions(rank); diff --git a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java index 2e3c23f..0e3422b 100644 --- a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java @@ -23,7 +23,7 @@ public final class UserMetaEntry { public UserMetaEntry(User user, ServerGroup serverGroup, Document data) { this.user = user.getId(); this.serverGroup = serverGroup.getId(); - this.data = data; + this.data = new Document(data); } public void delete() { diff --git a/src/main/java/net/frozenorb/apiv3/routes/GETRoutes.java b/src/main/java/net/frozenorb/apiv3/routes/GETRoutes.java deleted file mode 100644 index a157e89..0000000 --- a/src/main/java/net/frozenorb/apiv3/routes/GETRoutes.java +++ /dev/null @@ -1,72 +0,0 @@ -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; - - @SuppressWarnings("unchecked") // Casting List to List - 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/announcements/GETAnnouncements.java b/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java index 9c56e55..492d51e 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java +++ b/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java @@ -10,7 +10,7 @@ public final class GETAnnouncements implements Route { public Object handle(Request req, Response res) { Server sender = req.attribute("server"); - ServerGroup senderGroup = sender.resolveGroup(); + ServerGroup senderGroup = ServerGroup.byId(sender.getGroup()); return senderGroup.getAnnouncements(); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java b/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java index 700115f..0c3b60b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java +++ b/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java @@ -10,7 +10,7 @@ public final class GETChatFilterList implements Route { public Object handle(Request req, Response res) { Server sender = req.attribute("server"); - ServerGroup senderGroup = sender.resolveGroup(); + ServerGroup senderGroup = ServerGroup.byId(sender.getGroup()); return senderGroup.getChatFilterList(); } 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 9cf85a2..2cbaa92 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java @@ -18,7 +18,7 @@ public final class DELETEGrant implements Route { if (grant == null) { return ErrorUtils.notFound("Grant", req.params("id")); } else if (!grant.isActive()) { - return ErrorUtils.error("Cannot remove an inactive grant."); + return ErrorUtils.invalidInput("Cannot remove an inactive grant."); } User removedBy = User.byId(req.queryParams("removedBy")); @@ -33,7 +33,7 @@ public final class DELETEGrant implements Route { String reason = req.queryParams("removalReason"); grant.delete(removedBy, reason); - AuditLog.log(removedBy, req.attribute("actor"), "grant.remove", new Document("grantId", grant.getId())); + AuditLog.log(removedBy, req.attribute("actors"), "grant.remove", new Document("grantId", grant.getId())); return grant; } 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 32cc452..726e15b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java @@ -9,7 +9,7 @@ import spark.Route; public final class GETUserGrants implements Route { public Object handle(Request req, Response res) { - User target = User.byIdOrName(req.params("id")); + User target = User.byId(req.params("id")); if (target == null) { return ErrorUtils.notFound("User", req.params("id")); 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 bcb5cbd..67366b5 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java @@ -18,7 +18,7 @@ import java.util.Set; public final class POSTUserGrant implements Route { public Object handle(Request req, Response res) { - User target = User.byIdOrName(req.params("id")); + User target = User.byId(req.params("id")); if (target == null) { return ErrorUtils.notFound("User", req.params("id")); @@ -30,8 +30,17 @@ public final class POSTUserGrant implements Route { return ErrorUtils.invalidInput("A reason must be provided."); } - Set scopes = new HashSet<>(/*Arrays.asList(req.queryParams("scopes").split(","))*/); - // TODO + Set scopes = new HashSet<>(); + + for (String serverGroupId : req.queryParams("scopes").split(",")) { + ServerGroup serverGroup = ServerGroup.byId(serverGroupId); + + if (serverGroup == null) { + return ErrorUtils.notFound("Server group", serverGroupId); + } + + scopes.add(serverGroup); + } Rank rank = Rank.byId(req.queryParams("rank")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java b/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java index b26787d..13149ab 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java +++ b/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java @@ -9,7 +9,7 @@ import spark.Route; public final class GETUserIPLog implements Route { public Object handle(Request req, Response res) { - User target = User.byIdOrName(req.params("id")); + User target = User.byId(req.params("id")); if (target == null) { return ErrorUtils.notFound("User", req.params("id")); 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 946b814..6ae326f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java @@ -18,7 +18,7 @@ public final class DELETEPunishment implements Route { if (punishment == null) { return ErrorUtils.notFound("Punishment", req.params("id")); } else if (!punishment.isActive()) { - return ErrorUtils.error("Cannot remove an inactive punishment."); + return ErrorUtils.invalidInput("Cannot remove an inactive punishment."); } User removedBy = User.byId(req.queryParams("removedBy")); @@ -33,7 +33,7 @@ public final class DELETEPunishment implements Route { String reason = req.queryParams("removalReason"); punishment.delete(removedBy, reason); - AuditLog.log(removedBy, req.attribute("actor"), "punishment.remove", new Document("punishmentId", punishment.getId())); + AuditLog.log(removedBy, req.attribute("actors"), "punishment.remove", new Document("punishmentId", punishment.getId())); return punishment; } 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 98ecdaf..07479e0 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java @@ -15,7 +15,7 @@ import java.util.Date; public final class POSTUserPunish implements Route { public Object handle(Request req, Response res) { - User target = User.byIdOrName(req.params("id")); + User target = User.byId(req.params("id")); if (target == null) { return ErrorUtils.notFound("User", req.params("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/POSTHeartbeat.java b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java similarity index 82% rename from src/main/java/net/frozenorb/apiv3/routes/POSTHeartbeat.java rename to src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java index 37c5033..1eb704b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/POSTHeartbeat.java +++ b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java @@ -1,10 +1,10 @@ -package net.frozenorb.apiv3.routes; +package net.frozenorb.apiv3.routes.servers; import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.User; -import net.frozenorb.apiv3.unsorted.Actor; import net.frozenorb.apiv3.utils.ErrorUtils; import org.bson.Document; import spark.Request; @@ -13,10 +13,11 @@ import spark.Route; import java.util.*; -public final class POSTHeartbeat implements Route { +public final class POSTServerHeartbeat implements Route { + @SuppressWarnings("unchecked") public Object handle(Request req, Response res) { - Actor actor = req.attribute("actor"); + Actor actor = req.attribute("actors"); if (actor.getType() != Actor.Type.SERVER) { return ErrorUtils.error("Heartbeats can only be performed when requested by a server."); @@ -27,18 +28,19 @@ public final class POSTHeartbeat implements Route { Set onlinePlayers = new HashSet<>(); Map playersResponse = new HashMap<>(); - // TODO: SAW ON SERVER STUFF - for (Object player : (List) reqJson.get("players")) { Document playerJson = (Document) player; User user = User.byId(playerJson.getString("uuid")); String username = playerJson.getString("username"); if (user == null) { + // Will be saved by the save command a few lines down. user = new User(UUID.fromString(playerJson.getString("uuid")), username); - APIv3.getDatastore().save(user); } + user.seenOnServer(actorServer); + APIv3.getDatastore().save(user); + onlinePlayers.add(user.getId()); playersResponse.put(user.getId().toString(), user.getLoginInfo(actorServer)); } @@ -62,6 +64,7 @@ public final class POSTHeartbeat implements Route { actorServer.setPlayers(onlinePlayers); actorServer.setLastTps(reqJson.getDouble("lastTps")); actorServer.setLastUpdate(new Date()); + APIv3.getDatastore().save(actorServer); return ImmutableMap.of( "players", playersResponse diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java index 415bf1d..cfca7ce 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java @@ -11,7 +11,7 @@ import spark.Route; public final class DELETEUserMeta implements Route { public Object handle(Request req, Response res) { - User user = User.byIdOrName(req.params("id")); + User user = User.byId(req.params("id")); if (user == null) { return ErrorUtils.notFound("User", req.params("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java index c4361ef..42fefbb 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java @@ -8,7 +8,7 @@ import spark.Route; public final class GETUser implements Route { public Object handle(Request req, Response res) { - return User.byIdOrName(req.params("id")); + return User.byId(req.params("id")); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java index a454914..701ef24 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java @@ -10,7 +10,7 @@ import spark.Route; public final class GETUserDetails implements Route { public Object handle(Request req, Response res) { - User user = User.byIdOrName(req.params("id")); + User user = User.byId(req.params("id")); if (user == null) { return ErrorUtils.notFound("User", req.params("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java index 7d41fa6..90dbde4 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java @@ -11,7 +11,7 @@ import spark.Route; public final class GETUserMeta implements Route { public Object handle(Request req, Response res) { - User user = User.byIdOrName(req.params("id")); + User user = User.byId(req.params("id")); if (user == null) { return ErrorUtils.notFound("User", req.params("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/POSTConfirmRegister.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java similarity index 89% rename from src/main/java/net/frozenorb/apiv3/routes/POSTConfirmRegister.java rename to src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java index 9245900..76e002f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/POSTConfirmRegister.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.routes; +package net.frozenorb.apiv3.routes.users; import com.google.common.collect.ImmutableList; import net.frozenorb.apiv3.APIv3; @@ -12,9 +12,9 @@ import spark.Route; import java.util.List; import java.util.concurrent.TimeUnit; -public final class POSTConfirmRegister implements Route { +public final class POSTUserConfirmRegister implements Route { - private List commonPasswords = ImmutableList.copyOf(("123456 password 12345678 qwerty 123456789 12345 1234 111111 1234567 dragon " + + private final List commonPasswords = ImmutableList.copyOf(("123456 password 12345678 qwerty 123456789 12345 1234 111111 1234567 dragon " + "123123 baseball abc123 football monkey letmein 696969 shadow master 666666 qwertyuiop 123321 mustang 1234567890 " + "michael 654321 pussy superman 1qaz2wsx 7777777 fuckyou 121212 000000 qazwsx 123qwe killer trustno1 jordan jennifer " + "zxcvbnm asdfgh hunter buster soccer harley batman andrew tigger sunshine iloveyou fuckme 2000 charlie robert thomas " + diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLoginInfo.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLoginInfo.java index 33c07d9..c8486ca 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLoginInfo.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLoginInfo.java @@ -1,9 +1,9 @@ package net.frozenorb.apiv3.routes.users; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.User; -import net.frozenorb.apiv3.unsorted.Actor; import net.frozenorb.apiv3.utils.ErrorUtils; import spark.Request; import spark.Response; @@ -16,20 +16,21 @@ public final class POSTUserLoginInfo implements Route { public Object handle(Request req, Response res) { User user = User.byId(req.params("id")); String username = req.queryParams("username"); - Actor actor = req.attribute("actor"); + String userIp = req.queryParams("userIp"); + Actor actor = req.attribute("actors"); if (actor.getType() != Actor.Type.SERVER) { return ErrorUtils.error("Login info requests can only be performed when requested by a server."); } - // TODO: IP Logs - if (user == null) { user = new User(UUID.fromString(req.params("id")), username); APIv3.getDatastore().save(user); } Server actorServer = Server.byId(actor.getName()); + + user.getIPLogEntry(userIp).used(); return user.getLoginInfo(actorServer); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java index 1bee3ca..7672144 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java @@ -15,7 +15,7 @@ import java.util.Map; public final class POSTUserNotify implements Route { public Object handle(Request req, Response res) { - User user = User.byIdOrName(req.params("id")); + User user = User.byId(req.params("id")); if (user == null) { return ErrorUtils.notFound("User", req.params("id")); @@ -48,6 +48,7 @@ public final class POSTUserNotify implements Route { notification.sendAsEmail(user.getEmail()); return new Document("success", true); } catch (Exception ex) { + ex.printStackTrace(); return ErrorUtils.error("Failed to send notification"); } } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java index 7864dc8..60480de 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java @@ -20,18 +20,18 @@ import java.util.regex.Pattern; public final class POSTUserRegister implements Route { - public static final Pattern VALID_EMAIL_ADDRESS_REGEX = + private static final Pattern VALID_EMAIL_ADDRESS_REGEX = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); public Object handle(Request req, Response res) { - User user = User.byIdOrName(req.params("id")); + User user = User.byId(req.params("id")); if (user == null) { return ErrorUtils.notFound("User", req.params("id")); } if (user.getEmail() != null) { - return ErrorUtils.error("User provided already has email set."); + return ErrorUtils.invalidInput("User provided already has email set."); } String email = req.queryParams("email"); @@ -50,7 +50,7 @@ public final class POSTUserRegister implements Route { APIv3.getDatastore().save(user); Map replacements = ImmutableMap.of( - "username", user.getLastName(), + "username", user.getLastUsername(), "email", user.getEmail(), "emailToken", user.getEmailToken() ); @@ -61,6 +61,7 @@ public final class POSTUserRegister implements Route { notification.sendAsEmail(user.getEmail()); return new Document("success", true).append("message", "User registered"); } catch (Exception ex) { + ex.printStackTrace(); return ErrorUtils.error("Failed to send confirmation email. Please contact a MineHQ staff member."); } } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java index f2b24ac..3eded9f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java @@ -11,7 +11,7 @@ import spark.Route; public final class PUTUserMeta implements Route { public Object handle(Request req, Response res) { - User user = User.byIdOrName(req.params("id")); + User user = User.byId(req.params("id")); if (user == null) { return ErrorUtils.notFound("User", req.params("id")); diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/AuditLog.java b/src/main/java/net/frozenorb/apiv3/unsorted/AuditLog.java index 96cd555..03d7e79 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/AuditLog.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/AuditLog.java @@ -2,6 +2,7 @@ package net.frozenorb.apiv3.unsorted; import lombok.experimental.UtilityClass; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.models.AuditLogEntry; import net.frozenorb.apiv3.models.User; import org.bson.Document; diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java b/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java new file mode 100644 index 0000000..d016e3b --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java @@ -0,0 +1,20 @@ +package net.frozenorb.apiv3.unsorted; + +import net.frozenorb.apiv3.utils.ErrorUtils; +import org.bson.types.ObjectId; +import spark.ExceptionHandler; +import spark.Request; +import spark.Response; + +public final class LoggingExceptionHandler implements ExceptionHandler { + + public void handle(Exception ex, Request req, Response res) { + String code = new ObjectId().toHexString(); + + System.out.println(code + ":"); + ex.printStackTrace(); + + res.body(ErrorUtils.error("An unknown error has occurred. Please contact a developer with the code \"" + code + "\".").toJson()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java index 90dbf74..2f6980d 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java @@ -12,12 +12,12 @@ import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.io.IOException; import java.util.Map; -public final class Notification { +public final class Notification { - private static MandrillMessagesRequest messagesRequest = MandrillUtils.createMessagesRequest(); + private static final MandrillMessagesRequest messagesRequest = MandrillUtils.createMessagesRequest(); - private String subject; - private String body; + private final String subject; + private final String body; public Notification(NotificationTemplate template, Map subjectReplacements, Map bodyReplacements) { this.subject = template.fillSubject(subjectReplacements); diff --git a/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java b/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java index 00725a9..e82c280 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java @@ -1,22 +1,58 @@ package net.frozenorb.apiv3.utils; +import com.google.common.collect.ImmutableMap; import lombok.experimental.UtilityClass; import net.frozenorb.apiv3.models.Rank; -import net.frozenorb.apiv3.models.ServerGroup; +import java.util.HashMap; +import java.util.List; import java.util.Map; @UtilityClass public class PermissionUtils { - public static void mergePermissions(ServerGroup serverGroup, Rank rank, Map current) { - Map groupPermissions = serverGroup.getPermissions(rank); + public static Map mergePermissions(Map current, Map merge) { + Map result = new HashMap<>(current); - groupPermissions.forEach((permissionNode, grant) -> { - if (!current.containsKey(permissionNode) || !current.get(permissionNode)) { - current.put(permissionNode, grant); + result.putAll(merge); + + return result; + } + + public static Map mergeUpTo(Map> collection, Rank upTo) { + Map result = new HashMap<>(); + + for (Rank rank : Rank.values()) { + Map rankPermissions = convertToMap(collection.get(rank.getId())); + mergePermissions(result, rankPermissions); + + if (upTo.getId().equals(rank.getId())) { + break; } - }); + } + + return result; + } + + // TODO: Fix this + public static Map getDefaultPermissions(Rank rank) { + return ImmutableMap.of(); + } + + private static Map convertToMap(List unconvered) { + Map result = new HashMap<>(); + + for (String permission : unconvered) { + boolean negate = permission.startsWith("-"); + + if (negate) { + result.put(permission.substring(1), false); + } else { + result.put(permission, true); + } + } + + return result; } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/TimeUtils.java b/src/main/java/net/frozenorb/apiv3/utils/TimeUtils.java index cfe9b9f..09dce32 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/TimeUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/TimeUtils.java @@ -2,14 +2,11 @@ package net.frozenorb.apiv3.utils; import lombok.experimental.UtilityClass; -import java.text.SimpleDateFormat; import java.util.Date; @UtilityClass public class TimeUtils { - private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm"); - public static String formatIntoDetailedString(int secs) { if (secs == 0) { return "0 seconds"; @@ -30,10 +27,6 @@ public class TimeUtils { return (fDays + fHours + fMinutes + fSeconds).trim(); } - public static String formatIntoCalendarString(Date date) { - return (dateFormat.format(date)); - } - public static int getSecondsBetween(Date a, Date b) { return (Math.abs((int) (a.getTime() - b.getTime()) / 1000)); }