Whoa bunch of fixes
This commit is contained in:
parent
5845b6d631
commit
816b214993
5
pom.xml
5
pom.xml
@ -82,6 +82,11 @@
|
||||
<artifactId>morphia</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb.morphia</groupId>
|
||||
<artifactId>morphia-logging-slf4j</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@ -10,14 +10,14 @@ import lombok.Getter;
|
||||
import net.frozenorb.apiv3.filters.ActorAttributeFilter;
|
||||
import net.frozenorb.apiv3.filters.AuthorizationFilter;
|
||||
import net.frozenorb.apiv3.filters.ContentTypeFilter;
|
||||
import net.frozenorb.apiv3.models.Server;
|
||||
import net.frozenorb.apiv3.routes.GETDump;
|
||||
import net.frozenorb.apiv3.routes.GETWhoAmI;
|
||||
import net.frozenorb.apiv3.routes.NotFound;
|
||||
import net.frozenorb.apiv3.routes.announcements.GETAnnouncements;
|
||||
import net.frozenorb.apiv3.routes.auditLog.GETAuditLog;
|
||||
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.grants.*;
|
||||
import net.frozenorb.apiv3.routes.ipLog.GETUserIPLog;
|
||||
import net.frozenorb.apiv3.routes.notificationTemplate.DELETENotificationTemplate;
|
||||
import net.frozenorb.apiv3.routes.notificationTemplate.GETNotificationTemplate;
|
||||
@ -43,6 +43,9 @@ import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.Datastore;
|
||||
import org.mongodb.morphia.Morphia;
|
||||
import org.mongodb.morphia.logging.MorphiaLoggerFactory;
|
||||
import org.mongodb.morphia.logging.slf4j.SLF4JLoggerImplFactory;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
@ -60,6 +63,8 @@ public final class APIv3 {
|
||||
.create();
|
||||
|
||||
APIv3() {
|
||||
//System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace");
|
||||
|
||||
setupConfig();
|
||||
setupDatabase();
|
||||
setupHttp();
|
||||
@ -84,6 +89,9 @@ public final class APIv3 {
|
||||
config.getProperty("mongo.password").toCharArray())
|
||||
));
|
||||
|
||||
MorphiaLoggerFactory.reset();
|
||||
MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class);
|
||||
|
||||
Morphia morphia = new Morphia();
|
||||
morphia.mapPackage("net.frozenorb.apiv3.accessor");
|
||||
|
||||
@ -104,7 +112,9 @@ public final class APIv3 {
|
||||
get("/auditLog", new GETAuditLog(), gson::toJson);
|
||||
get("/chatFilterList", new GETChatFilterList(), gson::toJson);
|
||||
get("/dump/:type", new GETDump(), gson::toJson);
|
||||
get("/whoami", new GETWhoAmI(), gson::toJson);
|
||||
|
||||
get("/grant/:id", new GETGrant(), gson::toJson);
|
||||
get("/grants", new GETGrants(), gson::toJson);
|
||||
delete("/grant/:id", new DELETEGrant(), gson::toJson);
|
||||
|
||||
@ -151,6 +161,12 @@ public final class APIv3 {
|
||||
post("/user/confirmRegister/:emailToken", new POSTUserConfirmRegister(), gson::toJson);
|
||||
put("/user/:id/meta/:serverGroup", new PUTUserMeta(), gson::toJson);
|
||||
delete("/user/:id/meta", new DELETEUserMeta(), gson::toJson);
|
||||
|
||||
// There's no way to do a JSON 404 page w/o doing this :(
|
||||
get("/*", new NotFound(), gson::toJson);
|
||||
post("/*", new NotFound(), gson::toJson);
|
||||
put("/*", new NotFound(), gson::toJson);
|
||||
delete("/*", new NotFound(), gson::toJson);
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,7 @@ import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actors.*;
|
||||
import net.frozenorb.apiv3.models.Server;
|
||||
import net.frozenorb.apiv3.models.User;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Filter;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
@ -39,7 +40,7 @@ public final class ActorAttributeFilter implements Filter {
|
||||
}
|
||||
}
|
||||
|
||||
halt(401);
|
||||
halt(401, ErrorUtils.error("Failed to authorize.").toJson());
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -58,6 +59,11 @@ public final class ActorAttributeFilter implements Filter {
|
||||
}
|
||||
} else if (type.equals("Server") && split.length == 3) {
|
||||
Server server = Server.byId(split[1]);
|
||||
|
||||
if (server == null) {
|
||||
halt(401, ErrorUtils.notFound("Server", split[1]).toJson());
|
||||
}
|
||||
|
||||
String givenKey = split[2];
|
||||
String properKey = server.getApiKey();
|
||||
|
||||
@ -67,7 +73,7 @@ public final class ActorAttributeFilter implements Filter {
|
||||
}
|
||||
}
|
||||
|
||||
halt(401);
|
||||
halt(401, ErrorUtils.error("Failed to authorize.").toJson());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import spark.Spark;
|
||||
public final class AuthorizationFilter implements Filter {
|
||||
|
||||
public void handle(Request req, Response res) {
|
||||
Actor actor = req.attribute("actors");
|
||||
Actor actor = req.attribute("actor");
|
||||
|
||||
if (!actor.isAuthorized()) {
|
||||
Spark.halt(ErrorUtils.error("Unauthorized access: Please authenticate as either a server, the website, or an authorized user.").toJson());
|
||||
|
@ -16,7 +16,7 @@ public final class NotificationTemplate {
|
||||
@Getter private String body;
|
||||
|
||||
public static NotificationTemplate byId(String id) {
|
||||
return APIv3.getDatastore().createQuery(NotificationTemplate.class).field("id").equalIgnoreCase(id).get();
|
||||
return APIv3.getDatastore().createQuery(NotificationTemplate.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static List<NotificationTemplate> values() {
|
||||
@ -55,7 +55,7 @@ public final class NotificationTemplate {
|
||||
String key = replacement.getKey();
|
||||
String value = String.valueOf(replacement.getValue());
|
||||
|
||||
working = working.replace(key, value);
|
||||
working = working.replace("%" + key + "%", value);
|
||||
}
|
||||
|
||||
return working;
|
||||
|
@ -18,7 +18,7 @@ public final class Rank {
|
||||
@Getter private boolean staffRank;
|
||||
|
||||
public static Rank byId(String id) {
|
||||
return APIv3.getDatastore().createQuery(Rank.class).field("id").equalIgnoreCase(id).get();
|
||||
return APIv3.getDatastore().createQuery(Rank.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static List<Rank> values() {
|
||||
|
@ -3,6 +3,7 @@ package net.frozenorb.apiv3.models;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.serialization.ExcludeFromReplies;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
|
||||
@ -14,15 +15,15 @@ public final class Server {
|
||||
@Getter @Id private String id;
|
||||
@Getter private String bungeeId;
|
||||
@Getter private String displayName;
|
||||
@Getter private String apiKey;
|
||||
@Getter @ExcludeFromReplies String apiKey;
|
||||
@Getter private String group;
|
||||
@Getter private String ip;
|
||||
@Getter @Setter private Date lastUpdate;
|
||||
@Getter @Setter private double lastTps;
|
||||
@Getter @Setter private Set<UUID> players;
|
||||
@Getter @Setter @ExcludeFromReplies private Set<UUID> players;
|
||||
|
||||
public static Server byId(String id) {
|
||||
return APIv3.getDatastore().createQuery(Server.class).field("id").equalIgnoreCase(id).get();
|
||||
return APIv3.getDatastore().createQuery(Server.class).field("_id").equal(id).get();
|
||||
}
|
||||
|
||||
public static List<Server> values() {
|
||||
|
@ -14,12 +14,14 @@ public final class ServerGroup {
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private String displayName;
|
||||
@Getter private Set<String> announcements;
|
||||
@Getter private Set<String> chatFilterList;
|
||||
@Getter private Map<String, List<String>> permissions;
|
||||
// We define these HashSets up here because, in the event they're
|
||||
// empty, Morphia will load them as null, not empty sets.
|
||||
@Getter private Set<String> announcements = new HashSet<>();
|
||||
@Getter private Set<String> chatFilterList = new HashSet<>();
|
||||
@Getter private Map<String, List<String>> permissions = new HashMap<>();
|
||||
|
||||
public static ServerGroup byId(String id) {
|
||||
return APIv3.getDatastore().createQuery(ServerGroup.class).field("id").equalIgnoreCase(id).get();
|
||||
return APIv3.getDatastore().createQuery(ServerGroup.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static List<ServerGroup> values() {
|
||||
@ -31,9 +33,6 @@ public final class ServerGroup {
|
||||
public ServerGroup(String id, String displayName) {
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
this.announcements = new HashSet<>();
|
||||
this.chatFilterList = new HashSet<>();
|
||||
this.permissions = new HashMap<>();
|
||||
}
|
||||
|
||||
public void setAnnouncements(Set<String> announcements) {
|
||||
|
@ -34,7 +34,7 @@ public final class User {
|
||||
try {
|
||||
return byId(UUID.fromString(id));
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Invalid UUID string " + id, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ public final class User {
|
||||
}
|
||||
|
||||
public UserMetaEntry getMeta(ServerGroup group) {
|
||||
return APIv3.getDatastore().createQuery(UserMetaEntry.class).field("user").equal(id).field("serverGroup").equalIgnoreCase(group.getId()).get();
|
||||
return APIv3.getDatastore().createQuery(UserMetaEntry.class).field("user").equal(id).field("serverGroup").equal(group.getId()).get();
|
||||
}
|
||||
|
||||
public void saveMeta(ServerGroup group, Document data) {
|
||||
|
22
src/main/java/net/frozenorb/apiv3/routes/GETWhoAmI.java
Normal file
22
src/main/java/net/frozenorb/apiv3/routes/GETWhoAmI.java
Normal file
@ -0,0 +1,22 @@
|
||||
package net.frozenorb.apiv3.routes;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import org.bson.Document;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
|
||||
public final class GETWhoAmI implements Route {
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
Actor actor = req.attribute("actor");
|
||||
|
||||
return ImmutableMap.of(
|
||||
"name", actor.getName(),
|
||||
"type", actor.getType(),
|
||||
"authorized", actor.isAuthorized()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
19
src/main/java/net/frozenorb/apiv3/routes/NotFound.java
Normal file
19
src/main/java/net/frozenorb/apiv3/routes/NotFound.java
Normal file
@ -0,0 +1,19 @@
|
||||
package net.frozenorb.apiv3.routes;
|
||||
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.models.Grant;
|
||||
import net.frozenorb.apiv3.models.Punishment;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
import spark.Spark;
|
||||
|
||||
public final class NotFound implements Route {
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
Spark.halt(404, ErrorUtils.notFound("Route", req.url()).toJson());
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package net.frozenorb.apiv3.routes.announcements;
|
||||
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.models.Server;
|
||||
import net.frozenorb.apiv3.models.ServerGroup;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
@ -9,7 +11,13 @@ import spark.Route;
|
||||
public final class GETAnnouncements implements Route {
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
Server sender = req.attribute("server");
|
||||
Actor actor = req.attribute("actor");
|
||||
|
||||
if (actor.getType() != Actor.Type.SERVER) {
|
||||
return ErrorUtils.serverOnly();
|
||||
}
|
||||
|
||||
Server sender = Server.byId(actor.getName());
|
||||
ServerGroup senderGroup = ServerGroup.byId(sender.getGroup());
|
||||
|
||||
return senderGroup.getAnnouncements();
|
||||
|
@ -12,7 +12,7 @@ public final class GETAuditLog implements Route {
|
||||
int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
|
||||
int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
|
||||
|
||||
return APIv3.getDatastore().createQuery(AuditLogEntry.class).order("addedAt").limit(limit).offset(offset).asList();
|
||||
return APIv3.getDatastore().createQuery(AuditLogEntry.class).order("performedAt").limit(limit).offset(offset).asList();
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package net.frozenorb.apiv3.routes.chatFilterList;
|
||||
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.models.Server;
|
||||
import net.frozenorb.apiv3.models.ServerGroup;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
@ -9,7 +11,13 @@ import spark.Route;
|
||||
public final class GETChatFilterList implements Route {
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
Server sender = req.attribute("server");
|
||||
Actor actor = req.attribute("actor");
|
||||
|
||||
if (actor.getType() != Actor.Type.SERVER) {
|
||||
return ErrorUtils.serverOnly();
|
||||
}
|
||||
|
||||
Server sender = Server.byId(actor.getName());
|
||||
ServerGroup senderGroup = ServerGroup.byId(sender.getGroup());
|
||||
|
||||
return senderGroup.getChatFilterList();
|
||||
|
@ -33,7 +33,7 @@ public final class DELETEGrant implements Route {
|
||||
String reason = req.queryParams("removalReason");
|
||||
|
||||
grant.delete(removedBy, reason);
|
||||
AuditLog.log(removedBy, req.attribute("actors"), "grant.remove", new Document("grantId", grant.getId()));
|
||||
AuditLog.log(removedBy, req.attribute("actor"), "grant.remove", new Document("grantId", grant.getId()));
|
||||
return grant;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
package net.frozenorb.apiv3.routes.grants;
|
||||
|
||||
import net.frozenorb.apiv3.models.Grant;
|
||||
import net.frozenorb.apiv3.models.Punishment;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
|
||||
public final class GETGrant implements Route {
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
return Grant.byId(req.params("id"));
|
||||
}
|
||||
|
||||
}
|
@ -12,7 +12,7 @@ public final class GETGrants implements Route {
|
||||
int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
|
||||
int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
|
||||
|
||||
return APIv3.getDatastore().createQuery(Grant.class).order("performedAt").limit(limit).offset(offset).asList();
|
||||
return APIv3.getDatastore().createQuery(Grant.class).order("addedAt").limit(limit).offset(offset).asList();
|
||||
}
|
||||
|
||||
}
|
@ -33,7 +33,7 @@ public final class DELETEPunishment implements Route {
|
||||
String reason = req.queryParams("removalReason");
|
||||
|
||||
punishment.delete(removedBy, reason);
|
||||
AuditLog.log(removedBy, req.attribute("actors"), "punishment.remove", new Document("punishmentId", punishment.getId()));
|
||||
AuditLog.log(removedBy, req.attribute("actor"), "punishment.remove", new Document("punishmentId", punishment.getId()));
|
||||
return punishment;
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,10 @@ public final class POSTServerHeartbeat implements Route {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object handle(Request req, Response res) {
|
||||
Actor actor = req.attribute("actors");
|
||||
Actor actor = req.attribute("actor");
|
||||
|
||||
if (actor.getType() != Actor.Type.SERVER) {
|
||||
return ErrorUtils.error("Heartbeats can only be performed when requested by a server.");
|
||||
return ErrorUtils.serverOnly();
|
||||
}
|
||||
|
||||
Server actorServer = Server.byId(actor.getName());
|
||||
|
@ -29,7 +29,9 @@ public final class POSTUserConfirmRegister implements Route {
|
||||
return ErrorUtils.notFound("Email token", req.params("emailToken"));
|
||||
}
|
||||
|
||||
if (user.getEmail() != null) {
|
||||
// We can't check email != null as that's set while we're pending
|
||||
// confirmation, we have to check the token.
|
||||
if (user.getEmailToken() == null) {
|
||||
return ErrorUtils.error("User provided already has email set.");
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,10 @@ public final class POSTUserLoginInfo implements Route {
|
||||
User user = User.byId(req.params("id"));
|
||||
String username = req.queryParams("username");
|
||||
String userIp = req.queryParams("userIp");
|
||||
Actor actor = req.attribute("actors");
|
||||
Actor actor = req.attribute("actor");
|
||||
|
||||
if (actor.getType() != Actor.Type.SERVER) {
|
||||
return ErrorUtils.error("Login info requests can only be performed when requested by a server.");
|
||||
return ErrorUtils.serverOnly();
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
|
@ -1,17 +1,19 @@
|
||||
package net.frozenorb.apiv3.unsorted;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import org.bson.types.ObjectId;
|
||||
import spark.ExceptionHandler;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@Slf4j
|
||||
public final class LoggingExceptionHandler implements ExceptionHandler {
|
||||
|
||||
public void handle(Exception ex, Request req, Response res) {
|
||||
String code = new ObjectId().toHexString();
|
||||
|
||||
System.out.println(code + ":");
|
||||
log.error(code + ":");
|
||||
ex.printStackTrace();
|
||||
|
||||
res.body(ErrorUtils.error("An unknown error has occurred. Please contact a developer with the code \"" + code + "\".").toJson());
|
||||
|
@ -6,6 +6,10 @@ import org.bson.Document;
|
||||
@UtilityClass
|
||||
public class ErrorUtils {
|
||||
|
||||
public static Document serverOnly() {
|
||||
return error("This action can only be performed when requested by a server.");
|
||||
}
|
||||
|
||||
public static Document notFound(String itemType, String id) {
|
||||
return error("Not found: " + itemType + " with id " + id + " cannot be found.");
|
||||
}
|
||||
|
@ -19,11 +19,18 @@ public class PermissionUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<String, Boolean> mergeUpTo(Map<String, List<String>> collection, Rank upTo) {
|
||||
public static Map<String, Boolean> mergeUpTo(Map<String, List<String>> unconverted, Rank upTo) {
|
||||
Map<String, Boolean> result = new HashMap<>();
|
||||
|
||||
for (Rank rank : Rank.values()) {
|
||||
Map<String, Boolean> rankPermissions = convertToMap(collection.get(rank.getId()));
|
||||
List<String> unconvertedPermissions = unconverted.get(rank.getId());
|
||||
|
||||
// If there's no permissions defined for this rank just skip it.
|
||||
if (unconvertedPermissions == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Boolean> rankPermissions = convertToMap(unconvertedPermissions);
|
||||
mergePermissions(result, rankPermissions);
|
||||
|
||||
if (upTo.getId().equals(rank.getId())) {
|
||||
|
Loading…
Reference in New Issue
Block a user