Push a bunch of experimental code (this doesn't currently work/compile)

This commit is contained in:
Colin McDonald 2016-06-09 22:39:23 -04:00
parent 7d626afa20
commit 60a622fc43
80 changed files with 1156 additions and 841 deletions

View File

@ -8,9 +8,7 @@ mongo.username=
mongo.password= mongo.password=
redis.address=localhost redis.address=localhost
redis.port=6379 redis.port=6379
http.address=0.0.0.0
http.port=80 http.port=80
http.workerThreads=
twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740 twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740
twillio.authToken=982592505a171d3be6b0722f5ecacc0e twillio.authToken=982592505a171d3be6b0722f5ecacc0e
mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ

88
pom.xml
View File

@ -58,6 +58,7 @@
</repositories> </repositories>
<dependencies> <dependencies>
<!-- Vert.x -->
<dependency> <dependency>
<groupId>io.vertx</groupId> <groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId> <artifactId>vertx-core</artifactId>
@ -68,36 +69,39 @@
<artifactId>vertx-web</artifactId> <artifactId>vertx-web</artifactId>
<version>LATEST</version> <version>LATEST</version>
</dependency> </dependency>
<!-- Google Libs -->
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>19.0</version> <version>19.0</version>
</dependency> </dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.6.2</version> <version>2.6.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.indeed</groupId> <!-- Mongo -->
<artifactId>java-dogstatsd-client</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>com.bugsnag</groupId>
<artifactId>bugsnag</artifactId>
<version>2.0.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.mongodb</groupId> <groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId> <artifactId>mongodb-driver-async</artifactId>
<version>3.2.2</version> <version>3.0.4</version>
</dependency> </dependency>
<dependency>
<groupId>eu.dozd</groupId>
<artifactId>mongo-mapper</artifactId>
<version>1.0.1</version>
</dependency>
<!-- Redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<!-- Notifications -->
<dependency> <dependency>
<groupId>com.cribbstechnologies.clients</groupId> <groupId>com.cribbstechnologies.clients</groupId>
<artifactId>mandrillClient</artifactId> <artifactId>mandrillClient</artifactId>
@ -108,46 +112,34 @@
<artifactId>twilio-java-sdk</artifactId> <artifactId>twilio-java-sdk</artifactId>
<version>6.3.0</version> <version>6.3.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.mindrot</groupId> <!-- TOTP -->
<artifactId>jbcrypt</artifactId>
<version>0.3m</version>
</dependency>
<dependency>
<groupId>org.mongodb.morphia</groupId>
<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.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4</version>
</dependency>
<dependency> <dependency>
<groupId>com.warrenstrange</groupId> <groupId>com.warrenstrange</groupId>
<artifactId>googleauth</artifactId> <artifactId>googleauth</artifactId>
<version>0.5.0</version> <version>0.5.0</version>
</dependency> </dependency>
<!-- Monitoring -->
<dependency> <dependency>
<groupId>com.squareup.okhttp3</groupId> <groupId>com.indeed</groupId>
<artifactId>okhttp</artifactId> <artifactId>java-dogstatsd-client</artifactId>
<version>3.2.0</version> <version>2.0.12</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.squareup.okio</groupId> <groupId>com.bugsnag</groupId>
<artifactId>okio</artifactId> <artifactId>bugsnag</artifactId>
<version>1.8.0</version> <version>2.0.0</version>
</dependency> </dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
</dependency>
<!-- Lombok -->
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>

View File

@ -6,23 +6,36 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.mongodb.*; import com.mongodb.Block;
import com.mongodb.client.MongoDatabase; import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.async.client.MongoClient;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.async.client.MongoClients;
import com.mongodb.async.client.MongoDatabase;
import com.mongodb.connection.ClusterSettings;
import com.timgroup.statsd.NonBlockingStatsDClient; import com.timgroup.statsd.NonBlockingStatsDClient;
import com.timgroup.statsd.StatsDClient; import com.timgroup.statsd.StatsDClient;
import eu.dozd.mongo.MongoMapper;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpServer;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.LoggerHandler;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.actors.ActorType; import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.filters.*; import net.frozenorb.apiv3.filters.ActorAttributeFilter;
import net.frozenorb.apiv3.filters.AuthorizationFilter;
import net.frozenorb.apiv3.filters.MetricsHandler;
import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.IPLogEntry; import net.frozenorb.apiv3.models.IPLogEntry;
import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.routes.GETDump; import net.frozenorb.apiv3.routes.GETDump;
import net.frozenorb.apiv3.routes.GETWhoAmI; import net.frozenorb.apiv3.routes.GETWhoAmI;
import net.frozenorb.apiv3.routes.NotFound;
import net.frozenorb.apiv3.routes.POSTMetrics; import net.frozenorb.apiv3.routes.POSTMetrics;
import net.frozenorb.apiv3.routes.announcements.GETAnnouncements; import net.frozenorb.apiv3.routes.announcements.GETAnnouncements;
import net.frozenorb.apiv3.routes.auditLog.GETAuditLog; import net.frozenorb.apiv3.routes.auditLog.GETAuditLog;
@ -48,16 +61,11 @@ import net.frozenorb.apiv3.serialization.DateTypeAdapter;
import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy;
import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter; import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter;
import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger; import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger;
import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler;
import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.IPUtils;
import net.frozenorb.apiv3.utils.UUIDUtils; import net.frozenorb.apiv3.utils.UUIDUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import org.mongodb.morphia.converters.UUIDConverter;
import org.mongodb.morphia.logging.MorphiaLoggerFactory;
import org.mongodb.morphia.logging.slf4j.SLF4JLoggerImplFactory;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -66,13 +74,11 @@ import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static spark.Spark.*;
import static spark.route.RouteOverview.enableRouteOverview;
@Slf4j @Slf4j
public final class APIv3 { public final class APIv3 extends AbstractVerticle {
@Getter private static Datastore datastore; @Getter private static HttpClient httpClient;
@Getter private static MongoDatabase database;
@Getter private static Properties config = new Properties(); @Getter private static Properties config = new Properties();
@Getter private static JedisPool redisPool; @Getter private static JedisPool redisPool;
@Getter private static StatsDClient statsD; @Getter private static StatsDClient statsD;
@ -82,7 +88,8 @@ public final class APIv3 {
.setExclusionStrategies(new FollowAnnotationExclusionStrategy()) .setExclusionStrategies(new FollowAnnotationExclusionStrategy())
.create(); .create();
APIv3() { @Override
public void start() {
setupConfig(); setupConfig();
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", config.getProperty("logging.level")); System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", config.getProperty("logging.level"));
@ -90,10 +97,9 @@ public final class APIv3 {
setupRedis(); setupRedis();
setupMetrics(); setupMetrics();
setupBugsnag(); setupBugsnag();
setupHttp(); setupHttpServer();
//convertData("158.69.126.126", true); //convertData("158.69.126.126", true);
LoggingFilter.setDebug(Boolean.valueOf(config.getProperty("logging.debug")));
} }
private void setupConfig() { private void setupConfig() {
@ -108,28 +114,33 @@ public final class APIv3 {
ImmutableList<MongoCredential> credentials = ImmutableList.of(); ImmutableList<MongoCredential> credentials = ImmutableList.of();
if (!config.getProperty("mongo.username").isEmpty()) { if (!config.getProperty("mongo.username").isEmpty()) {
credentials = ImmutableList.of(MongoCredential.createCredential( credentials = ImmutableList.of(
config.getProperty("mongo.username"), MongoCredential.createCredential(
config.getProperty("mongo.database"), config.getProperty("mongo.username"),
config.getProperty("mongo.password").toCharArray() config.getProperty("mongo.database"),
)); config.getProperty("mongo.password").toCharArray()
)
);
} }
MongoClient mongoClient = new MongoClient(new ServerAddress( ClusterSettings clusterSettings = ClusterSettings
config.getProperty("mongo.address"), .builder()
Integer.parseInt(config.getProperty("mongo.port"))), .hosts(ImmutableList.of(
credentials new ServerAddress(
); config.getProperty("mongo.address"),
Integer.parseInt(config.getProperty("mongo.port"))
)
))
.build();
MongoClientSettings settings = MongoClientSettings
.builder()
.codecRegistry(CodecRegistries.fromProviders(MongoMapper.getProviders()))
.credentialList(credentials)
.clusterSettings(clusterSettings).build();
MorphiaLoggerFactory.reset(); MongoClient client = MongoClients.create(settings);
MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class); database = client.getDatabase(config.getProperty("mongo.database"));
// TODO: Indexes
Morphia morphia = new Morphia();
morphia.mapPackage("net.frozenorb.apiv3.models");
morphia.getMapper().getConverters().addConverter(new UUIDConverter());
datastore = morphia.createDatastore(mongoClient, config.getProperty("mongo.database"));
datastore.ensureIndexes();
} }
private void setupRedis() { private void setupRedis() {
@ -160,103 +171,105 @@ public final class APIv3 {
bugsnag.setLogger(new BugsnagSLF4JLogger()); bugsnag.setLogger(new BugsnagSLF4JLogger());
} }
private void setupHttp() { private void setupHttpServer() {
Router.router() HttpServer webServer = vertx.createHttpServer();
ipAddress(config.getProperty("http.address")); Router mainRouter = Router.router(vertx);
port(Integer.parseInt(config.getProperty("http.port")));
String workerThreads = config.getProperty("http.workerThreads");
if (!workerThreads.isEmpty()) { mainRouter.route().handler(new MetricsHandler());
threadPool(Integer.parseInt(workerThreads)); mainRouter.route().handler(new ActorAttributeFilter());
} mainRouter.route().handler(new AuthorizationFilter());
mainRouter.route().handler(LoggerHandler.create());
before(new MetricsBeforeFilter()); mainRouter.route().handler(BodyHandler.create());
before(new ContentTypeFilter());
before(new ActorAttributeFilter());
before(new AuthorizationFilter());
after(new MetricsAfterFilter());
after(new LoggingFilter());
exception(Exception.class, new LoggingExceptionHandler());
// TODO: The commented out routes // TODO: The commented out routes
post("/metrics", new POSTMetrics(), gson::toJson); mainRouter.post("/metrics").blockingHandler(new POSTMetrics());
get("/announcements", new GETAnnouncements(), gson::toJson); mainRouter.get("/announcements").blockingHandler(new GETAnnouncements());
get("/auditLog", new GETAuditLog(), gson::toJson); mainRouter.get("/auditLog").blockingHandler(new GETAuditLog());
get("/chatFilterList", new GETChatFilterList(), gson::toJson); mainRouter.get("/chatFilterList").blockingHandler(new GETChatFilterList());
get("/dump/:type", new GETDump(), gson::toJson); mainRouter.get("/dump/:type").blockingHandler(new GETDump());
get("/whoami", new GETWhoAmI(), gson::toJson); mainRouter.get("/whoami").blockingHandler(new GETWhoAmI());
enableRouteOverview("/routes");
get("/grant/:id", new GETGrant(), gson::toJson); mainRouter.get("/grant/:id").blockingHandler(new GETGrant());
get("/grants", new GETGrants(), gson::toJson); mainRouter.get("/grants").blockingHandler(new GETGrants());
delete("/grant/:id", new DELETEGrant(), gson::toJson); mainRouter.delete("/grant/:id").blockingHandler(new DELETEGrant());
get("/notificationTemplate/:id", new GETNotificationTemplate(), gson::toJson); mainRouter.get("/notificationTemplate/:id").blockingHandler(new GETNotificationTemplate());
get("/notificationTemplates", new GETNotificationTemplates(), gson::toJson); mainRouter.get("/notificationTemplates").blockingHandler(new GETNotificationTemplates());
post("/notificationTemplate", new POSTNotificationTemplate(), gson::toJson); mainRouter.post("/notificationTemplate").blockingHandler(new POSTNotificationTemplate());
//put("/notificationTemplate/:id", new PUTNotificationTemplate(), gson::toJson); //mainRouter.put("/notificationTemplate/:id").blockingHandler(new PUTNotificationTemplate());
delete("/notificationTemplate/:id", new DELETENotificationTemplate(), gson::toJson); mainRouter.delete("/notificationTemplate/:id").blockingHandler(new DELETENotificationTemplate());
get("/punishment/:id", new GETPunishment(), gson::toJson); mainRouter.get("/punishment/:id").blockingHandler(new GETPunishment());
get("/punishments", new GETPunishments(), gson::toJson); mainRouter.get("/punishments").blockingHandler(new GETPunishments());
delete("/punishment/:id", new DELETEPunishment(), gson::toJson); mainRouter.delete("/punishment/:id").blockingHandler(new DELETEPunishment());
get("/rank/:id", new GETRank(), gson::toJson); mainRouter.get("/rank/:id").blockingHandler(new GETRank());
get("/ranks", new GETRanks(), gson::toJson); mainRouter.get("/ranks").blockingHandler(new GETRanks());
post("/rank", new POSTRank(), gson::toJson); mainRouter.post("/rank").blockingHandler(new POSTRank());
//put("/rank/:id", new PUTRank(), gson::toJson); //put("/rank/:id").blockingHandler(new PUTRank());
delete("/rank/:id", new DELETERank(), gson::toJson); mainRouter.delete("/rank/:id").blockingHandler(new DELETERank());
get("/serverGroup/:id", new GETServerGroup(), gson::toJson); mainRouter.get("/serverGroup/:id").blockingHandler(new GETServerGroup());
get("/serverGroups", new GETServerGroups(), gson::toJson); mainRouter.get("/serverGroups").blockingHandler(new GETServerGroups());
post("/serverGroup", new POSTServerGroup(), gson::toJson); mainRouter.post("/serverGroup").blockingHandler(new POSTServerGroup());
//put("/serverGroup/:id", new PUTServerGroup(), gson::toJson); //put("/serverGroup/:id").blockingHandler(new PUTServerGroup());
delete("/serverGroup/:id", new DELETEServerGroup(), gson::toJson); mainRouter.delete("/serverGroup/:id").blockingHandler(new DELETEServerGroup());
get("/server/:id", new GETServer(), gson::toJson); mainRouter.get("/server/:id").blockingHandler(new GETServer());
get("/servers", new GETServers(), gson::toJson); mainRouter.get("/servers").blockingHandler(new GETServers());
post("/server/heartbeat", new POSTServerHeartbeat(), gson::toJson); mainRouter.post("/server/heartbeat").blockingHandler(new POSTServerHeartbeat());
post("/server", new POSTServer(), gson::toJson); mainRouter.post("/server").blockingHandler(new POSTServer());
//put("/server/:id", new PUTServer(), gson::toJson); //put("/server/:id").blockingHandler(new PUTServer());
delete("/server/:id", new DELETEServer(), gson::toJson); mainRouter.delete("/server/:id").blockingHandler(new DELETEServer());
get("/staff", new GETStaff(), gson::toJson); mainRouter.get("/staff").blockingHandler(new GETStaff());
get("/user/:id/details", new GETUserDetails(), gson::toJson); mainRouter.get("/user/:id/details").blockingHandler(new GETUserDetails());
get("/user/:id/meta/:serverGroup", new GETUserMeta(), gson::toJson); mainRouter.get("/user/:id/meta/:serverGroup").blockingHandler(new GETUserMeta());
get("/user/:id/grants", new GETUserGrants(), gson::toJson); mainRouter.get("/user/:id/grants").blockingHandler(new GETUserGrants());
get("/user/:id/punishments", new GETUserPunishments(), gson::toJson); mainRouter.get("/user/:id/punishments").blockingHandler(new GETUserPunishments());
get("/user/:id/ipLog", new GETUserIPLog(), gson::toJson); mainRouter.get("/user/:id/ipLog").blockingHandler(new GETUserIPLog());
get("/user/:id/requiresTOTP", new GETUserRequiresTOTP(), gson::toJson); mainRouter.get("/user/:id/requiresTOTP").blockingHandler(new GETUserRequiresTOTP());
get("/user/:id/verifyPassword", new GETUserVerifyPassword(), gson::toJson); mainRouter.get("/user/:id/verifyPassword").blockingHandler(new GETUserVerifyPassword());
get("/user/:id", new GETUser(), gson::toJson); mainRouter.get("/user/:id").blockingHandler(new GETUser());
post("/user/:id/verifyTOTP", new POSTUserVerifyTOTP(), gson::toJson); mainRouter.post("/user/:id/verifyTOTP").blockingHandler(new POSTUserVerifyTOTP());
post("/user/:id/grant", new POSTUserGrant(), gson::toJson); mainRouter.post("/user/:id/grant").blockingHandler(new POSTUserGrant());
post("/user/:id/punish", new POSTUserPunish(), gson::toJson); mainRouter.post("/user/:id/punish").blockingHandler(new POSTUserPunish());
post("/user/:id/login", new POSTUserLogin(), gson::toJson); mainRouter.post("/user/:id/login").blockingHandler(new POSTUserLogin());
post("/user/:id/leave", new POSTUserLeave(), gson::toJson); mainRouter.post("/user/:id/leave").blockingHandler(new POSTUserLeave());
post("/user/:id/notify", new POSTUserNotify(), gson::toJson); mainRouter.post("/user/:id/notify").blockingHandler(new POSTUserNotify());
post("/user/:id/register", new POSTUserRegister(), gson::toJson); mainRouter.post("/user/:id/register").blockingHandler(new POSTUserRegister());
post("/user/:id/setupTOTP", new POSTUserSetupTOTP(), gson::toJson); mainRouter.post("/user/:id/setupTOTP").blockingHandler(new POSTUserSetupTOTP());
post("/user/confirmRegister/:emailToken", new POSTUserConfirmRegister(), gson::toJson); mainRouter.post("/user/confirmRegister/:emailToken").blockingHandler(new POSTUserConfirmRegister());
put("/user/:id/meta/:serverGroup", new PUTUserMeta(), gson::toJson); mainRouter.put("/user/:id/meta/:serverGroup").blockingHandler(new PUTUserMeta());
delete("/user/:id/meta/:serverGroup", new DELETEUserMeta(), gson::toJson); mainRouter.delete("/user/:id/meta/:serverGroup").blockingHandler(new DELETEUserMeta());
delete("/user/:id/punishment", new DELETEUserPunishment(), gson::toJson); mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment());
// There's no way to do a JSON 404 page w/o doing this :( mainRouter.getRoutes().forEach((route) -> {
get("/*", new NotFound(), gson::toJson); System.out.println(route.getClass() + "||" + route.getPath());
post("/*", new NotFound(), gson::toJson); });
put("/*", new NotFound(), gson::toJson);
delete("/*", new NotFound(), gson::toJson); int port = Integer.parseInt(config.getProperty("http.port"));
webServer.requestHandler(mainRouter::accept).listen(port);
} }
public static void respond(RoutingContext ctx, Object response) { private void setupHttpClient() {
httpClient = vertx.createHttpClient();
}
public static void respondJson(RoutingContext ctx, Object response) {
respondJson(ctx, 200, response);
}
public static void respondJson(RoutingContext ctx, int code, Object response) {
ctx.response().putHeader("Content-Type", "application/json");
ctx.response().setStatusCode(code);
ctx.response().end(gson.toJson(response)); ctx.response().end(gson.toJson(response));
} }
private void convertData(String oldIp, boolean forReal) { private void convertData(String oldIp, boolean forReal) {
// A lot of unneeded .toString()'s and cloning objects is our ghetto null validation. // A lot of unneeded .toString()'s and cloning objects is our ghetto null validation.
MongoDatabase importFrom = new MongoClient(oldIp).getDatabase("minehq"); MongoDatabase importFrom = MongoClients.create(oldIp).getDatabase("minehq");
Map<ObjectId, UUID> mongoIdToUUID = new HashMap<>(); Map<ObjectId, UUID> mongoIdToUUID = new HashMap<>();
AtomicInteger skippedUsers = new AtomicInteger(); AtomicInteger skippedUsers = new AtomicInteger();
AtomicInteger skippedPunishments = new AtomicInteger(); AtomicInteger skippedPunishments = new AtomicInteger();
@ -299,12 +312,12 @@ public final class APIv3 {
); );
if (forReal) { if (forReal) {
APIv3.getDatastore().save(created); created.insert();
} }
log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")"); log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")");
} }
}); }, (a, b) -> {});
importFrom.getCollection("punishment").find().forEach(new Block<Document>() { importFrom.getCollection("punishment").find().forEach(new Block<Document>() {
@ -322,6 +335,8 @@ public final class APIv3 {
return; return;
} }
com.mongodb.
Punishment created = new Punishment( Punishment created = new Punishment(
new ObjectId().toString(), new ObjectId().toString(),
target, target,
@ -339,12 +354,12 @@ public final class APIv3 {
); );
if (forReal) { if (forReal) {
APIv3.getDatastore().save(created); created.insert();
} }
log.info("Created punishment " + created.getId() + " (" + created.getType() + ")"); log.info("Created punishment " + created.getId() + " (" + created.getType() + ")");
} }
}); }, (a, b) -> {});
importFrom.getCollection("grant").find().forEach(new Block<Document>() { importFrom.getCollection("grant").find().forEach(new Block<Document>() {
@ -382,12 +397,12 @@ public final class APIv3 {
); );
if (forReal) { if (forReal) {
APIv3.getDatastore().save(created); created.insert();
} }
log.info("Created grant " + created.getId() + " (" + created.getRank() + ")"); log.info("Created grant " + created.getId() + " (" + created.getRank() + ")");
} }
}); }, (a, b) -> {});
importFrom.getCollection("iplog").find().forEach(new Block<Document>() { importFrom.getCollection("iplog").find().forEach(new Block<Document>() {
@ -423,12 +438,12 @@ public final class APIv3 {
); );
if (forReal) { if (forReal) {
APIv3.getDatastore().save(created); created.insert();
} }
log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")"); log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")");
} }
}); }, (a, b) -> {});
log.info("Skipped " + skippedUsers.get() + " users, " + skippedPunishments.get() + " punishments, " + skippedGrants.get() + " grants, and " + skippedIpLogs.get() + " ip logs"); log.info("Skipped " + skippedUsers.get() + " users, " + skippedPunishments.get() + " punishments, " + skippedGrants.get() + " grants, and " + skippedIpLogs.get() + " ip logs");
} }

View File

@ -1,9 +1,11 @@
package net.frozenorb.apiv3; package net.frozenorb.apiv3;
import io.vertx.core.Vertx;
final class Main { final class Main {
public static void main(String[] args) { public static void main(String[] args) {
new APIv3(); Vertx.vertx().deployVerticle(new APIv3());
} }
} }

View File

@ -23,7 +23,7 @@ public final class UserActor implements Actor {
if (cachedAuthorized != null) { if (cachedAuthorized != null) {
return cachedAuthorized; return cachedAuthorized;
} else { } else {
String highestRankId = user.getHighestRank().getId(); String highestRankId = user.getHighestRankAnywhere().getId();
cachedAuthorized = permittedUserRanks.contains(highestRankId.toLowerCase()); cachedAuthorized = permittedUserRanks.contains(highestRankId.toLowerCase());
return cachedAuthorized; return cachedAuthorized;
} }

View File

@ -17,8 +17,9 @@ public class AuditLog {
} }
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, Map<String, Object> actionData) { public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, Map<String, Object> actionData) {
AuditLogEntry entry = new AuditLogEntry(performedBy, performedByIp, actor, actionType, actionData);
entry.insert();
APIv3.getStatsD().incrementCounter("apiv3.auditLog.insertions"); APIv3.getStatsD().incrementCounter("apiv3.auditLog.insertions");
APIv3.getDatastore().save(new AuditLogEntry(performedBy, performedByIp, actor, actionType, actionData));
} }
} }

View File

@ -1,52 +1,53 @@
package net.frozenorb.apiv3.filters; package net.frozenorb.apiv3.filters;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.*; import net.frozenorb.apiv3.actors.*;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Filter;
import spark.Request;
import spark.Response;
import spark.Spark;
import java.util.Base64; import java.util.Base64;
public final class ActorAttributeFilter implements Filter { public final class ActorAttributeFilter implements Handler<RoutingContext> {
public void handle(Request req, Response res) { // TODO: MAKE THIS ASYNC
String authHeader = req.headers("Authorization"); public void handle(RoutingContext ctx) {
String mhqAuthHeader = req.headers("MHQ-Authorization"); String authHeader = ctx.request().getHeader("Authorization");
String mhqAuthHeader = ctx.request().getHeader("MHQ-Authorization");
if (authHeader != null) { if (authHeader != null) {
req.attribute("actor", processBasicAuthorization(authHeader, res)); processBasicAuthorization(authHeader, ctx);
} else if (mhqAuthHeader != null) { } else if (mhqAuthHeader != null) {
req.attribute("actor", processMHQAuthorization(mhqAuthHeader)); processMHQAuthorization(mhqAuthHeader, ctx);
} else { } else {
req.attribute("actor", new UnknownActor()); ctx.put("actor", new UnknownActor());
ctx.next();
} }
} }
@SuppressWarnings("deprecation") // We purposely get the User by their last username. @SuppressWarnings("deprecation") // We purposely get the User by their last username.
private Actor processBasicAuthorization(String authHeader, Response res) { private void processBasicAuthorization(String authHeader, RoutingContext ctx) {
String encodedHeader = authHeader.substring("Basic ".length()); String encodedHeader = authHeader.substring("Basic ".length());
String[] credentials = new String(Base64.getDecoder().decode(encodedHeader.getBytes())).split(":"); String[] credentials = new String(Base64.getDecoder().decode(encodedHeader.getBytes())).split(":");
if (credentials.length == 2) { if (credentials.length == 2) {
User user = User.byLastUsername(credentials[0]); User user = User.findByLastUsername(credentials[0]);
String password = credentials[1]; String password = credentials[1];
if (user != null && user.getPassword() != null && user.checkPassword(password)) { if (user != null && user.getPassword() != null && user.checkPassword(password)) {
return new UserActor(user); ctx.put("actor", new UserActor(user));
ctx.next();
return;
} }
} }
res.header("WWW-Authenticate", "Basic realm=\"MineHQ\""); ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\"");
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Failed to authorize as " + credentials[0] + "."))); ErrorUtils.respondGeneric(ctx, "Failed to authorize as " + credentials[0] + ".");
return null;
} }
private Actor processMHQAuthorization(String authHeader) { private void processMHQAuthorization(String authHeader, RoutingContext ctx) {
String[] split = authHeader.split(" "); String[] split = authHeader.split(" ");
if (split.length >= 2) { if (split.length >= 2) {
@ -57,33 +58,39 @@ public final class ActorAttributeFilter implements Filter {
String properKey = APIv3.getConfig().getProperty("auth.websiteApiKey"); String properKey = APIv3.getConfig().getProperty("auth.websiteApiKey");
if (givenKey.equals(properKey)) { if (givenKey.equals(properKey)) {
return new WebsiteActor(); ctx.put("actor", new WebsiteActor());
ctx.next();
return;
} }
} else if (type.equals("Server") && split.length == 3) { } else if (type.equals("Server") && split.length == 3) {
Server server = Server.byId(split[1]); Server server = Server.findById(split[1]);
if (server == null) { if (server == null) {
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.respondNotFound("Server", split[1]))); ErrorUtils.respondNotFound(ctx, "Server", split[1]);
return;
} }
String givenKey = split[2]; String givenKey = split[2];
String properKey = server.getApiKey(); String properKey = server.getApiKey();
if (givenKey.equals(properKey)) { if (givenKey.equals(properKey)) {
return new ServerActor(server); ctx.put("actor", new ServerActor(server));
ctx.next();
return;
} }
} else if (type.equals("BungeeCord") && split.length == 2) { } else if (type.equals("BungeeCord") && split.length == 2) {
String givenKey = split[1]; String givenKey = split[1];
String properKey = APIv3.getConfig().getProperty("auth.bungeeCordApiKey"); String properKey = APIv3.getConfig().getProperty("auth.bungeeCordApiKey");
if (givenKey.equals(properKey)) { if (givenKey.equals(properKey)) {
return new BungeeCordActor(); ctx.put("actor", new BungeeCordActor());
ctx.next();
return;
} }
} }
} }
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Failed to authorize."))); ErrorUtils.respondGeneric(ctx, "Failed to authorize.");
return null;
} }
} }

View File

@ -1,22 +1,22 @@
package net.frozenorb.apiv3.filters; package net.frozenorb.apiv3.filters;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Filter;
import spark.Request;
import spark.Response;
import spark.Spark;
public final class AuthorizationFilter implements Filter { public final class AuthorizationFilter implements Handler<RoutingContext> {
public void handle(Request req, Response res) { public void handle(RoutingContext ctx) {
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");
if (!actor.isAuthorized()) { if (!actor.isAuthorized()) {
APIv3.getStatsD().incrementCounter("apiv3.http.unauthorized"); APIv3.getStatsD().incrementCounter("apiv3.http.unauthorized");
res.header("WWW-Authenticate", "Basic realm=\"MineHQ\""); ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\"");
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Unauthorized access: Please authorize as an approved actor. You're currently authorized as " + actor.getName()))); ErrorUtils.respondGeneric(ctx, "Unauthorized access: Please authorize as an approved actor. You're currently authorized as " + actor.getName());
} else {
ctx.next();
} }
} }

View File

@ -1,13 +0,0 @@
package net.frozenorb.apiv3.filters;
import spark.Filter;
import spark.Request;
import spark.Response;
public final class ContentTypeFilter implements Filter {
public void handle(Request req, Response res) {
res.type("application/json");
}
}

View File

@ -1,21 +0,0 @@
package net.frozenorb.apiv3.filters;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.actors.Actor;
import spark.Filter;
import spark.Request;
import spark.Response;
@Slf4j
public final class LoggingFilter implements Filter {
@Getter @Setter private static boolean debug = false;
public void handle(Request req, Response res) {
Actor actor = ctx.get("actor");
log.info("(" + actor.getName() + " - " + actor.getType() + ") " + req.requestMethod().toUpperCase() + " " + req.pathInfo() + (debug ? "\n" + res.body() : ""));
}
}

View File

@ -1,16 +0,0 @@
package net.frozenorb.apiv3.filters;
import net.frozenorb.apiv3.APIv3;
import spark.Filter;
import spark.Request;
import spark.Response;
public final class MetricsAfterFilter implements Filter {
public void handle(Request req, Response res) {
long started = req.attribute("requestStarted");
APIv3.getStatsD().recordExecutionTime("apiv3.http.executionTime", System.currentTimeMillis() - started);
APIv3.getStatsD().incrementCounter("apiv3.http.requests");
}
}

View File

@ -1,13 +0,0 @@
package net.frozenorb.apiv3.filters;
import spark.Filter;
import spark.Request;
import spark.Response;
public final class MetricsBeforeFilter implements Filter {
public void handle(Request req, Response res) {
req.attribute("requestStarted", System.currentTimeMillis());
}
}

View File

@ -0,0 +1,14 @@
package net.frozenorb.apiv3.filters;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
public final class MetricsHandler implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
APIv3.getStatsD().incrementCounter("apiv3.http.requests");
ctx.next();
}
}

View File

@ -1,31 +1,55 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.mongodb.async.client.MongoCollection;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.actors.ActorType; import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.auditLog.AuditLog;
import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.auditLog.AuditLogActionType;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Indexed;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Entity(value = "auditLog", noClassnameStored = true) @Entity
public final class AuditLogEntry { public final class AuditLogEntry {
private static final MongoCollection<AuditLogEntry> auditLogCollection = APIv3.getDatabase().getCollection("auditLog", AuditLogEntry.class);
@Getter @Id private String id; @Getter @Id private String id;
@Getter @Indexed private UUID user; @Getter private UUID user;
@Getter private String userIp; @Getter private String userIp;
@Getter @Indexed private Date performedAt; @Getter private Date performedAt;
@Getter private String actorName; @Getter private String actorName;
@Getter private ActorType actorType; @Getter private ActorType actorType;
@Getter private AuditLogActionType type; @Getter private AuditLogActionType type;
@Getter private Map<String, Object> metadata; @Getter private Map<String, Object> metadata;
public static List<AuditLogEntry> findAll() {
return SyncUtils.blockMulti(auditLogCollection.find());
}
public static AuditLogEntry findById(String id) {
return SyncUtils.blockOne(auditLogCollection.find(new Document("_id", id)));
}
public static List<AuditLogEntry> findByUser(User user) {
return findByUser(user.getId());
}
public static List<AuditLogEntry> findByUser(UUID user) {
return SyncUtils.blockMulti(auditLogCollection.find(new Document("user", user)));
}
public AuditLogEntry() {} // For Morphia public AuditLogEntry() {} // For Morphia
public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map<String, Object> metadata) { public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map<String, Object> metadata) {
@ -39,4 +63,10 @@ public final class AuditLogEntry {
this.metadata = ImmutableMap.copyOf(metadata); this.metadata = ImmutableMap.copyOf(metadata);
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
auditLogCollection.insertOne(this, callback);
callback.get();
}
} }

View File

@ -1,56 +1,59 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.utils.UUIDUtils; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Indexed;
import java.util.*; import java.util.*;
@Entity(value = "grants", noClassnameStored = true) @Entity
@AllArgsConstructor @AllArgsConstructor
public final class Grant { public final class Grant {
private static final MongoCollection<Grant> grantsCollection = APIv3.getDatabase().getCollection("grants", Grant.class);
@Getter @Id private String id; @Getter @Id private String id;
@Getter @Indexed private UUID user; @Getter private UUID user;
@Getter private String reason; @Getter private String reason;
@Getter private Set<String> scopes = new HashSet<>(); // So on things w/o scopes we still load properly (Morphia drops empty sets) @Getter private Set<String> scopes = new HashSet<>(); // So on things w/o scopes we still load properly (Morphia drops empty sets)
@Getter @Indexed private String rank; @Getter private String rank;
@Getter private Date expiresAt; @Getter private Date expiresAt;
@Getter private UUID addedBy; @Getter private UUID addedBy;
@Getter @Indexed private Date addedAt; @Getter private Date addedAt;
@Getter private UUID removedBy; @Getter private UUID removedBy;
@Getter private Date removedAt; @Getter private Date removedAt;
@Getter private String removalReason; @Getter private String removalReason;
public static Grant byId(String id) { public static List<Grant> findAll() {
return APIv3.getDatastore().createQuery(Grant.class).field("id").equal(id).get(); return SyncUtils.blockMulti(grantsCollection.find());
} }
public static Map<UUID, List<Grant>> byUserGrouped(Iterable<UUID> ids) { public static List<Grant> findByRank(Iterable<Rank> ranks) {
Map<UUID, List<Grant>> result = new HashMap<>(); return SyncUtils.blockMulti(grantsCollection.find(new Document("rank", new Document("$in", ranks))));
Set<UUID> uuidsToSearch = new HashSet<>(); }
for (UUID id : ids) { public static Grant findById(String id) {
result.put(id, new ArrayList<>()); return SyncUtils.blockOne(grantsCollection.find(new Document("_id", id)));
}
if (UUIDUtils.isAcceptableUUID(id)) { public static List<Grant> findByUser(User user) {
uuidsToSearch.add(id); return findByUser(user.getId());
} }
}
APIv3.getDatastore().createQuery(Grant.class).field("user").in(uuidsToSearch).forEach((grant) -> { public static List<Grant> findByUser(UUID user) {
result.get(grant.getUser()).add(grant); return SyncUtils.blockMulti(grantsCollection.find(new Document("user", user)));
});
return result;
} }
public Grant() {} // For Morphia public Grant() {} // For Morphia
@ -66,14 +69,6 @@ public final class Grant {
this.addedAt = new Date(); this.addedAt = new Date();
} }
public void delete(User removedBy, String reason) {
this.removedBy = removedBy.getId();
this.removedAt = new Date();
this.removalReason = reason;
APIv3.getDatastore().save(this);
}
public boolean isActive() { public boolean isActive() {
return !(isExpired() || isRemoved()); return !(isExpired() || isRemoved());
} }
@ -98,4 +93,20 @@ public final class Grant {
return scopes.isEmpty(); return scopes.isEmpty();
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
grantsCollection.insertOne(this, callback);
callback.get();
}
public void delete(User removedBy, String reason) {
this.removedBy = removedBy.getId();
this.removedAt = new Date();
this.removalReason = reason;
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
grantsCollection.deleteOne(new Document("_id", id), callback);
callback.get();
}
} }

View File

@ -1,31 +1,57 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.mongodb.async.client.MongoCollection;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.*;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@Entity(value = "ipLog", noClassnameStored = true) @Entity
@AllArgsConstructor @AllArgsConstructor
@Indexes(
@Index(fields = {
@Field("user"),
@Field("userIp")
})
)
public final class IPLogEntry { public final class IPLogEntry {
private static final MongoCollection<IPLogEntry> ipLogCollection = APIv3.getDatabase().getCollection("ipLog", IPLogEntry.class);
@Getter @Id private String id; @Getter @Id private String id;
@Getter @Indexed private UUID user; @Getter private UUID user;
@Getter @Indexed private String userIp; @Getter private String userIp;
@Getter private Date firstSeenAt; @Getter private Date firstSeenAt;
@Getter private Date lastSeenAt; @Getter private Date lastSeenAt;
@Getter private int uses; @Getter private int uses;
public static List<IPLogEntry> findAll() {
return SyncUtils.blockMulti(ipLogCollection.find());
}
public static IPLogEntry findById(String id) {
return SyncUtils.blockOne(ipLogCollection.find(new Document("_id", id)));
}
public static List<IPLogEntry> findByUser(User user) {
return findByUser(user.getId());
}
public static List<IPLogEntry> findByUser(UUID user) {
return SyncUtils.blockMulti(ipLogCollection.find(new Document("user", user)));
}
public static IPLogEntry findByUserAndIp(User user, String userIp) {
return findByUserAndIp(user.getId(), userIp);
}
public static IPLogEntry findByUserAndIp(UUID user, String userIp) {
return SyncUtils.blockOne(ipLogCollection.find(new Document("user", user).append("userIp", userIp)));
}
public IPLogEntry() {} // For Morphia public IPLogEntry() {} // For Morphia
public IPLogEntry(User user, String userIp) { public IPLogEntry(User user, String userIp) {
@ -40,8 +66,12 @@ public final class IPLogEntry {
public void used() { public void used() {
this.lastSeenAt = new Date(); this.lastSeenAt = new Date();
this.uses++; this.uses++;
}
APIv3.getDatastore().save(this); public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
ipLogCollection.insertOne(this, callback);
callback.get();
} }
} }

View File

@ -1,26 +1,35 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import org.mongodb.morphia.annotations.Entity; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import org.mongodb.morphia.annotations.Id; import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Entity(value = "notificationTemplates", noClassnameStored = true) @Entity
public final class NotificationTemplate { public final class NotificationTemplate {
@Getter @Id private String id; private static final MongoCollection<NotificationTemplate> notificationTemplatesCollection = APIv3.getDatabase().getCollection("notificationTemplates", NotificationTemplate.class);
@Getter private String subject;
@Getter private String body;
public static NotificationTemplate byId(String id) { @Getter @Id private String id;
return APIv3.getDatastore().createQuery(NotificationTemplate.class).field("id").equal(id).get(); @Getter @Setter private String subject;
@Getter @Setter private String body;
public static List<NotificationTemplate> findAll() {
return SyncUtils.blockMulti(notificationTemplatesCollection.find());
} }
public static List<NotificationTemplate> values() { public static NotificationTemplate findById(String id) {
return APIv3.getDatastore().createQuery(NotificationTemplate.class).asList(); return SyncUtils.blockOne(notificationTemplatesCollection.find(new Document("_id", id)));
} }
public NotificationTemplate() {} // For Morphia public NotificationTemplate() {} // For Morphia
@ -31,17 +40,6 @@ public final class NotificationTemplate {
this.body = body; this.body = body;
} }
public void update(String subject, String body) {
this.subject = subject;
this.body = body;
APIv3.getDatastore().save(this);
}
public void delete() {
APIv3.getDatastore().delete(this);
}
public String fillSubject(Map<String, Object> replacements) { public String fillSubject(Map<String, Object> replacements) {
return fill(subject, replacements); return fill(subject, replacements);
} }
@ -61,4 +59,22 @@ public final class NotificationTemplate {
return working; return working;
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
notificationTemplatesCollection.insertOne(this, callback);
callback.get();
}
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
notificationTemplatesCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
public void delete() {
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
notificationTemplatesCollection.deleteOne(new Document("_id", id), callback);
callback.get();
}
} }

View File

@ -1,37 +1,37 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.google.common.collect.ImmutableSet; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.actors.ActorType; import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.SyncUtils;
import net.frozenorb.apiv3.utils.TimeUtils; import net.frozenorb.apiv3.utils.TimeUtils;
import net.frozenorb.apiv3.utils.UUIDUtils; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.*;
import java.util.*; import java.util.*;
@Entity(value = "punishments", noClassnameStored = true) @Entity
@AllArgsConstructor @AllArgsConstructor
@Indexes(
@Index(fields = {
@Field("user"),
@Field("type")
})
)
public final class Punishment { public final class Punishment {
private static final MongoCollection<Punishment> punishmentsCollection = APIv3.getDatabase().getCollection("punishments", Punishment.class);
@Getter @Id private String id; @Getter @Id private String id;
@Getter @Indexed private UUID user; @Getter private UUID user;
@Getter private String reason; @Getter private String reason;
@Getter @Indexed private PunishmentType type; // Type is indexed for the rank dump @Getter private PunishmentType type;
@Getter private Date expiresAt; @Getter private Date expiresAt;
@Getter private Map<String, Object> metadata; @Getter private Map<String, Object> metadata;
@Getter private UUID addedBy; @Getter private UUID addedBy;
@Getter @Indexed private Date addedAt; @Getter private Date addedAt;
@Getter private String actorName; @Getter private String actorName;
@Getter private ActorType actorType; @Getter private ActorType actorType;
@ -39,31 +39,32 @@ public final class Punishment {
@Getter private Date removedAt; @Getter private Date removedAt;
@Getter private String removalReason; @Getter private String removalReason;
public static Punishment byId(String id) { public static List<Punishment> findAll() {
return APIv3.getDatastore().createQuery(Punishment.class).field("id").equal(id).get(); return SyncUtils.blockMulti(punishmentsCollection.find());
} }
public static Map<UUID, List<Punishment>> byUserGrouped(Iterable<UUID> ids) { public static List<Punishment> findByType(Iterable<PunishmentType> types) {
return byUserGrouped(ids, ImmutableSet.copyOf(PunishmentType.values())); return SyncUtils.blockMulti(punishmentsCollection.find(new Document("type", new Document("$in", types))));
} }
public static Map<UUID, List<Punishment>> byUserGrouped(Iterable<UUID> ids, Iterable<Punishment.PunishmentType> types) { public static Punishment findById(String id) {
Map<UUID, List<Punishment>> result = new HashMap<>(); return SyncUtils.blockOne(punishmentsCollection.find(new Document("_id", id)));
Set<UUID> uuidsToSearch = new HashSet<>(); }
for (UUID id : ids) { public static List<Punishment> findByUser(User user) {
result.put(id, new ArrayList<>()); return findByUser(user.getId());
}
if (UUIDUtils.isAcceptableUUID(id)) { public static List<Punishment> findByUser(UUID user) {
uuidsToSearch.add(id); return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user)));
} }
}
APIv3.getDatastore().createQuery(Punishment.class).field("user").in(uuidsToSearch).field("type").in(types).forEach((punishment) -> { public static List<Punishment> findByUserAndType(User user, Iterable<PunishmentType> types) {
result.get(punishment.getUser()).add(punishment); return findByUserAndType(user.getId(), types);
}); }
return result; public static List<Punishment> findByUserAndType(UUID user, Iterable<PunishmentType> types) {
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", types))));
} }
public Punishment() {} // For Morphia public Punishment() {} // For Morphia
@ -81,14 +82,6 @@ public final class Punishment {
this.metadata = metadata; this.metadata = metadata;
} }
public void delete(User removedBy, String reason) {
this.removedBy = removedBy.getId();
this.removedAt = new Date();
this.removalReason = reason;
APIv3.getDatastore().save(this);
}
public boolean isActive() { public boolean isActive() {
return !(isExpired() || isRemoved()); return !(isExpired() || isRemoved());
} }
@ -124,6 +117,22 @@ public final class Punishment {
} }
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
punishmentsCollection.insertOne(this, callback);
callback.get();
}
public void delete(User removedBy, String reason) {
this.removedBy = removedBy.getId();
this.removedAt = new Date();
this.removalReason = reason;
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
punishmentsCollection.deleteOne(new Document("_id", id), callback);
callback.get();
}
public enum PunishmentType { public enum PunishmentType {
BLACKLIST, BAN, MUTE, WARN BLACKLIST, BAN, MUTE, WARN

View File

@ -1,11 +1,17 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import org.mongodb.morphia.annotations.Entity; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import org.mongodb.morphia.annotations.Id; import net.frozenorb.apiv3.utils.SyncUtils;
import org.mongodb.morphia.annotations.Indexed; import org.bson.Document;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -13,9 +19,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Entity(value = "ranks", noClassnameStored = true) @Entity
public final class Rank { public final class Rank {
private static final MongoCollection<Rank> ranksCollection = APIv3.getDatabase().getCollection("ranks", Rank.class);
private static Map<String, Rank> rankCache = null; private static Map<String, Rank> rankCache = null;
private static List<Rank> rankAltCache = null; private static List<Rank> rankAltCache = null;
private static long rankCacheUpdated = 0; private static long rankCacheUpdated = 0;
@ -25,18 +33,18 @@ public final class Rank {
@Getter private String displayName; @Getter private String displayName;
@Getter private String gameColor; @Getter private String gameColor;
@Getter private String websiteColor; @Getter private String websiteColor;
@Getter @Indexed private boolean staffRank; @Getter private boolean staffRank;
public static Rank byId(String id) { public static List<Rank> findAll() {
updateCacheIfNeeded();
return rankCache.get(id);
}
public static List<Rank> values() {
updateCacheIfNeeded(); updateCacheIfNeeded();
return ImmutableList.copyOf(rankAltCache); return ImmutableList.copyOf(rankAltCache);
} }
public static Rank findById(String id) {
updateCacheIfNeeded();
return rankCache.get(id);
}
public Rank() {} // For Morphia public Rank() {} // For Morphia
public Rank(String id, int weight, String displayName, String gameColor, String websiteColor, boolean staffRank) { public Rank(String id, int weight, String displayName, String gameColor, String websiteColor, boolean staffRank) {
@ -48,16 +56,14 @@ public final class Rank {
this.staffRank = staffRank; this.staffRank = staffRank;
} }
public void delete() {
APIv3.getDatastore().delete(this);
}
private static void updateCacheIfNeeded() { private static void updateCacheIfNeeded() {
if (rankCache == null || (System.currentTimeMillis() - rankCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) { if (rankCache == null || (System.currentTimeMillis() - rankCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) {
Map<String, Rank> working = new HashMap<>(); Map<String, Rank> working = new HashMap<>();
List<Rank> workingAlt = new ArrayList<>(); List<Rank> workingAlt = new ArrayList<>();
List<Rank> allRanks = SyncUtils.blockMulti(ranksCollection.find());
allRanks.sort((a, b) -> Ints.compare(a.getWeight(), b.getWeight()));
for (Rank rank : APIv3.getDatastore().createQuery(Rank.class).order("weight").asList()) { for (Rank rank : allRanks) {
working.put(rank.getId(), rank); working.put(rank.getId(), rank);
workingAlt.add(rank); workingAlt.add(rank);
} }
@ -68,4 +74,22 @@ public final class Rank {
} }
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
ranksCollection.insertOne(this, callback);
callback.get();
}
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
ranksCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
public void delete() {
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
ranksCollection.deleteOne(new Document("_id", id), callback);
callback.get();
}
} }

View File

@ -1,19 +1,27 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.google.common.collect.ImmutableSet;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.serialization.ExcludeFromReplies; import net.frozenorb.apiv3.serialization.ExcludeFromReplies;
import org.mongodb.morphia.annotations.Entity; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import org.mongodb.morphia.annotations.Id; import net.frozenorb.apiv3.utils.SyncUtils;
import org.mongodb.morphia.annotations.Indexed; import org.bson.Document;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Entity(value = "servers", noClassnameStored = true) @Entity
public final class Server { public final class Server {
private static final MongoCollection<Server> serversCollection = APIv3.getDatabase().getCollection("servers", Server.class);
private static Map<String, Server> serverCache = null; private static Map<String, Server> serverCache = null;
private static List<Server> serverCacheAlt = null; private static List<Server> serverCacheAlt = null;
private static long serverCacheUpdated = 0; private static long serverCacheUpdated = 0;
@ -21,22 +29,22 @@ public final class Server {
@Getter @Id private String id; @Getter @Id private String id;
@Getter private String displayName; @Getter private String displayName;
@Getter @ExcludeFromReplies String apiKey; @Getter @ExcludeFromReplies String apiKey;
@Getter @Indexed private String serverGroup; @Getter private String serverGroup;
@Getter private String serverIp; @Getter private String serverIp;
@Getter @Setter private Date lastUpdatedAt; @Getter @Setter private Date lastUpdatedAt;
@Getter @Setter private double lastTps; @Getter @Setter private double lastTps;
@Getter @Setter @ExcludeFromReplies private Set<UUID> players; @Getter @Setter @ExcludeFromReplies private Set<UUID> players;
public static Server byId(String id) { public static List<Server> findAll() {
updateCacheIfNeeded();
return serverCache.get(id);
}
public static List<Server> values() {
updateCacheIfNeeded(); updateCacheIfNeeded();
return serverCacheAlt; return serverCacheAlt;
} }
public static Server findById(String id) {
updateCacheIfNeeded();
return serverCache.get(id);
}
public Server() {} // For Morphia public Server() {} // For Morphia
public Server(String id, String displayName, String apiKey, ServerGroup serverGroup, String serverIp) { public Server(String id, String displayName, String apiKey, ServerGroup serverGroup, String serverIp) {
@ -50,16 +58,12 @@ public final class Server {
this.players = new HashSet<>(); this.players = new HashSet<>();
} }
public void delete() {
APIv3.getDatastore().delete(this);
}
private static void updateCacheIfNeeded() { private static void updateCacheIfNeeded() {
if (serverCache == null || (System.currentTimeMillis() - serverCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) { if (serverCache == null || (System.currentTimeMillis() - serverCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) {
Map<String, Server> working = new HashMap<>(); Map<String, Server> working = new HashMap<>();
List<Server> workingAlt = new ArrayList<>(); List<Server> workingAlt = new ArrayList<>();
for (Server server : APIv3.getDatastore().createQuery(Server.class).asList()) { for (Server server : SyncUtils.blockMulti(serversCollection.find())) {
working.put(server.getId(), server); working.put(server.getId(), server);
workingAlt.add(server); workingAlt.add(server);
} }
@ -70,4 +74,28 @@ public final class Server {
} }
} }
public void receivedHeartbeat(double tps, Iterable<UUID> players) {
this.lastUpdatedAt = new Date();
this.lastTps = tps;
this.players = ImmutableSet.copyOf(players);
}
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
serversCollection.insertOne(this, callback);
callback.get();
}
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
serversCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
public void delete() {
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
serversCollection.deleteOne(new Document("_id", id), callback);
callback.get();
}
} }

View File

@ -1,44 +1,51 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.serialization.ExcludeFromReplies; import net.frozenorb.apiv3.serialization.ExcludeFromReplies;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.PermissionUtils; import net.frozenorb.apiv3.utils.PermissionUtils;
import org.mongodb.morphia.annotations.Entity; import net.frozenorb.apiv3.utils.SyncUtils;
import org.mongodb.morphia.annotations.Id; import org.bson.Document;
import org.mongodb.morphia.annotations.Property;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Entity(value = "serverGroups", noClassnameStored = true) @Entity
public final class ServerGroup { public final class ServerGroup {
private static final MongoCollection<ServerGroup> serverGroupsCollection = APIv3.getDatabase().getCollection("serverGroups", ServerGroup.class);
private static Map<String, ServerGroup> serverGroupCache = null; private static Map<String, ServerGroup> serverGroupCache = null;
private static List<ServerGroup> serverGroupAltCache = null; private static List<ServerGroup> serverGroupAltCache = null;
private static long serverGroupCacheUpdated = 0; private static long serverGroupCacheUpdated = 0;
@Getter @Id private String id; @Getter @Id private String id;
@Getter private String image; @Getter private String image;
// We rename this to public, we just can't name it that because it's a Java identifier. // We rename this to public (only to gson), we just can't name it that because it's a Java identifier.
@Getter @Property("public") @SerializedName("public") private boolean isPublic; @Getter @SerializedName("public") private boolean isPublic;
// We define these HashSets up here because, in the event they're // We define these HashSets up here because, in the event they're
// empty, Morphia will load them as null, not empty sets. // empty, Morphia will load them as null, not empty sets.
@Getter @Setter @ExcludeFromReplies private Set<String> announcements = new HashSet<>(); @Getter @Setter @ExcludeFromReplies private Set<String> announcements = new HashSet<>();
@Getter @Setter @ExcludeFromReplies private Map<String, List<String>> permissions = new HashMap<>(); @Getter @Setter @ExcludeFromReplies private Map<String, List<String>> permissions = new HashMap<>();
public static ServerGroup byId(String id) { public static List<ServerGroup> findAll() {
updateCacheIfNeeded();
return serverGroupCache.get(id);
}
public static List<ServerGroup> values() {
updateCacheIfNeeded(); updateCacheIfNeeded();
return serverGroupAltCache; return serverGroupAltCache;
} }
public static ServerGroup findById(String id) {
updateCacheIfNeeded();
return serverGroupCache.get(id);
}
public ServerGroup() {} // For Morphia public ServerGroup() {} // For Morphia
public ServerGroup(String id, String image, boolean isPublic) { public ServerGroup(String id, String image, boolean isPublic) {
@ -51,16 +58,12 @@ public final class ServerGroup {
return PermissionUtils.mergeUpTo(permissions, userRank); return PermissionUtils.mergeUpTo(permissions, userRank);
} }
public void delete() {
APIv3.getDatastore().delete(this);
}
private static void updateCacheIfNeeded() { private static void updateCacheIfNeeded() {
if (serverGroupCache == null || (System.currentTimeMillis() - serverGroupCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) { if (serverGroupCache == null || (System.currentTimeMillis() - serverGroupCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) {
Map<String, ServerGroup> working = new HashMap<>(); Map<String, ServerGroup> working = new HashMap<>();
List<ServerGroup> workingAlt = new ArrayList<>(); List<ServerGroup> workingAlt = new ArrayList<>();
for (ServerGroup serverGroup : APIv3.getDatastore().createQuery(ServerGroup.class).asList()) { for (ServerGroup serverGroup : SyncUtils.blockMulti(serverGroupsCollection.find())) {
working.put(serverGroup.getId(), serverGroup); working.put(serverGroup.getId(), serverGroup);
workingAlt.add(serverGroup); workingAlt.add(serverGroup);
} }
@ -71,4 +74,22 @@ public final class ServerGroup {
} }
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
serverGroupsCollection.insertOne(this, callback);
callback.get();
}
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
serverGroupsCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
public void delete() {
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
serverGroupsCollection.deleteOne(new Document("_id", id), callback);
callback.get();
}
} }

View File

@ -4,30 +4,35 @@ import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.serialization.ExcludeFromReplies; import net.frozenorb.apiv3.serialization.ExcludeFromReplies;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.MojangUtils; import net.frozenorb.apiv3.utils.MojangUtils;
import net.frozenorb.apiv3.utils.PermissionUtils; import net.frozenorb.apiv3.utils.PermissionUtils;
import net.frozenorb.apiv3.utils.SyncUtils;
import net.frozenorb.apiv3.utils.UUIDUtils; import net.frozenorb.apiv3.utils.UUIDUtils;
import org.bson.Document; import org.bson.Document;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Indexed;
import java.util.*; import java.util.*;
@Entity(value = "users", noClassnameStored = true) @Entity
@AllArgsConstructor @AllArgsConstructor
public final class User { public final class User {
private static final MongoCollection<User> usersCollection = APIv3.getDatabase().getCollection("users", User.class);
@Getter @Id private UUID id; @Getter @Id private UUID id;
@Getter @Indexed private String lastUsername; @Getter private String lastUsername;
@Getter @ExcludeFromReplies private Map<String, Date> aliases = new HashMap<>(); @Getter @ExcludeFromReplies private Map<String, Date> aliases = new HashMap<>();
@Getter @Setter @ExcludeFromReplies private String totpSecret; @Getter @ExcludeFromReplies @Setter private String totpSecret;
@Getter @Indexed @ExcludeFromReplies @Setter private String emailToken; @Getter @ExcludeFromReplies @Setter private String emailToken;
@Getter @ExcludeFromReplies @Setter private Date emailTokenSetAt; @Getter @ExcludeFromReplies @Setter private Date emailTokenSetAt;
@Getter @ExcludeFromReplies private String password; @Getter @ExcludeFromReplies private String password;
@Getter @Setter private String email; @Getter @Setter private String email;
@ -37,48 +42,36 @@ public final class User {
@Getter private Date firstSeenAt; @Getter private Date firstSeenAt;
@Getter private boolean online; @Getter private boolean online;
public static User byId(String id) { public static List<User> findAll() {
try { return SyncUtils.blockMulti(usersCollection.find());
return byId(UUID.fromString(id));
} catch (Exception ex) {
return null;
}
} }
public static User byId(UUID id) { public static User findById(String id) {
UUID uuid;
try {
uuid = UUID.fromString(id);
} catch (IllegalArgumentException ex) {
return null;
}
return findById(uuid);
}
public static User findById(UUID id) {
if (UUIDUtils.isAcceptableUUID(id)) { if (UUIDUtils.isAcceptableUUID(id)) {
return APIv3.getDatastore().createQuery(User.class).field("id").equal(id).get(); return SyncUtils.blockOne(usersCollection.find(new Document("_id", id)));
} else { } else {
return null; return null;
} }
} }
public static Map<UUID, User> byIdGrouped(Iterable<UUID> ids) { public static User findByEmailToken(String emailToken) {
Map<UUID, User> result = new HashMap<>(); return SyncUtils.blockOne(usersCollection.find(new Document("emailToken", emailToken)));
Set<UUID> uuidsToSearch = new HashSet<>();
for (UUID id : ids) {
result.put(id, null);
if (UUIDUtils.isAcceptableUUID(id)) {
uuidsToSearch.add(id);
}
}
APIv3.getDatastore().createQuery(User.class).field("id").in(uuidsToSearch).forEach((user) -> {
result.put(user.getId(), user);
});
return result;
} }
public static User byEmailToken(String name) { public static User findByLastUsername(String lastUsername) {
return APIv3.getDatastore().createQuery(User.class).field("emailToken").equal(name).get(); return SyncUtils.blockOne(usersCollection.find(new Document("lastUsername", lastUsername)));
}
@Deprecated
public static User byLastUsername(String lastUsername) {
return APIv3.getDatastore().createQuery(User.class).field("lastUsername").equal(lastUsername).get();
} }
public User() {} // For Morphia public User() {} // For Morphia
@ -98,18 +91,8 @@ public final class User {
updateUsername(lastUsername); updateUsername(lastUsername);
} }
public boolean hasPermissionScoped(String permission, ServerGroup scope) {
Rank highestRank = getHighestRank(scope);
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
PermissionUtils.getDefaultPermissions(highestRank),
scope.calculatePermissions(highestRank)
);
return scopedPermissions.containsKey(permission) && scopedPermissions.get(permission);
}
public boolean hasPermissionAnywhere(String permission) { public boolean hasPermissionAnywhere(String permission) {
Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRank()); Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRankAnywhere());
for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks().entrySet()) { for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks().entrySet()) {
ServerGroup serverGroup = serverGroupEntry.getKey(); ServerGroup serverGroup = serverGroupEntry.getKey();
@ -124,48 +107,7 @@ public final class User {
return globalPermissions.containsKey(permission) && globalPermissions.get(permission); return globalPermissions.containsKey(permission) && globalPermissions.get(permission);
} }
public List<Grant> getGrants() { // TODO: Clean
return APIv3.getDatastore().createQuery(Grant.class).field("user").equal(id).asList();
}
public List<IPLogEntry> getIPLog() {
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("userIp").equal(ip).get();
if (existing == null) {
existing = new IPLogEntry(this, ip);
APIv3.getDatastore().save(existing);
}
return existing;
}
public List<Punishment> getPunishments() {
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).asList();
}
public List<Punishment> getPunishments(Iterable<Punishment.PunishmentType> types) {
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).field("type").in(types).asList();
}
public UserMetaEntry getMeta(ServerGroup group) {
return APIv3.getDatastore().createQuery(UserMetaEntry.class).field("user").equal(id).field("serverGroup").equal(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);
}
}
public boolean seenOnServer(Server server) { public boolean seenOnServer(Server server) {
if (online && server.getId().equals(this.lastSeenOn)) { if (online && server.getId().equals(this.lastSeenOn)) {
return false; return false;
@ -192,14 +134,13 @@ public final class User {
User withNewUsername; User withNewUsername;
while ((withNewUsername = User.byLastUsername(username)) != null) { while ((withNewUsername = User.findByLastUsername(username)) != null) {
String newUsername = MojangUtils.getName(withNewUsername.getId()); String newUsername = MojangUtils.getName(withNewUsername.getId());
withNewUsername.updateUsername(newUsername); withNewUsername.updateUsername(newUsername);
} }
} }
this.aliases.put(username, new Date()); this.aliases.put(username, new Date());
APIv3.getDatastore().save(this);
} }
public void setPassword(String input) { public void setPassword(String input) {
@ -218,16 +159,17 @@ public final class User {
return hashed.equals(password); return hashed.equals(password);
} }
public Rank getHighestRank() { public Rank getHighestRankAnywhere() {
return getHighestRank(null); return getHighestRankScoped(null);
} }
public Rank getHighestRank(ServerGroup serverGroup) { public Rank getHighestRankScoped(ServerGroup serverGroup) {
return getHighestRank(serverGroup, getGrants()); return getHighestRankScoped(serverGroup, Grant.findByUser(this));
} }
// TODO: Clean
// This is only used to help batch requests to mongo // This is only used to help batch requests to mongo
public Rank getHighestRank(ServerGroup serverGroup, Iterable<Grant> grants) { public Rank getHighestRankScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
Rank highest = null; Rank highest = null;
for (Grant grant : grants) { for (Grant grant : grants) {
@ -235,7 +177,7 @@ public final class User {
continue; continue;
} }
Rank rank = Rank.byId(grant.getRank()); Rank rank = Rank.findById(grant.getRank());
if (highest == null || rank.getWeight() > highest.getWeight()) { if (highest == null || rank.getWeight() > highest.getWeight()) {
highest = rank; highest = rank;
@ -245,16 +187,17 @@ public final class User {
if (highest != null) { if (highest != null) {
return highest; return highest;
} else { } else {
return Rank.byId("default"); return Rank.findById("default");
} }
} }
// TODO: Clean
public Map<ServerGroup, Rank> getHighestRanks() { public Map<ServerGroup, Rank> getHighestRanks() {
Map<ServerGroup, Rank> highestRanks = new HashMap<>(); Map<ServerGroup, Rank> highestRanks = new HashMap<>();
Rank defaultRank = Rank.byId("default"); Rank defaultRank = Rank.findById("default");
List<Grant> userGrants = getGrants(); List<Grant> userGrants = Grant.findByUser(this);
for (ServerGroup serverGroup : ServerGroup.values()) { for (ServerGroup serverGroup : ServerGroup.findAll()) {
Rank highest = defaultRank; Rank highest = defaultRank;
for (Grant grant : userGrants) { for (Grant grant : userGrants) {
@ -262,7 +205,7 @@ public final class User {
continue; continue;
} }
Rank rank = Rank.byId(grant.getRank()); Rank rank = Rank.findById(grant.getRank());
if (highest == null || rank.getWeight() > highest.getWeight()) { if (highest == null || rank.getWeight() > highest.getWeight()) {
highest = rank; highest = rank;
@ -276,19 +219,19 @@ public final class User {
} }
public Map<String, Object> getLoginInfo(Server server) { public Map<String, Object> getLoginInfo(Server server) {
return getLoginInfo( return createLoginInfo(
server, server,
getPunishments(ImmutableSet.of( Punishment.findByUserAndType(this, ImmutableSet.of(
Punishment.PunishmentType.BLACKLIST, Punishment.PunishmentType.BLACKLIST,
Punishment.PunishmentType.BAN, Punishment.PunishmentType.BAN,
Punishment.PunishmentType.MUTE Punishment.PunishmentType.MUTE
)), )),
getGrants() Grant.findByUser(this)
); );
} }
// This is only used to help batch requests to mongo // This is only used to help batch requests to mongo
public Map<String, Object> getLoginInfo(Server server, Iterable<Punishment> punishments, Iterable<Grant> grants) { public Map<String, Object> createLoginInfo(Server server, Iterable<Punishment> punishments, Iterable<Grant> grants) {
Punishment activeMute = null; Punishment activeMute = null;
String accessDenialReason = null; String accessDenialReason = null;
@ -304,7 +247,7 @@ public final class User {
} }
} }
Rank highestRank = getHighestRank(ServerGroup.byId(server.getServerGroup()), grants); Rank highestRank = getHighestRankScoped(ServerGroup.findById(server.getServerGroup()), grants);
// Generics are weird, yes we have to do this. // Generics are weird, yes we have to do this.
ImmutableMap.Builder<String, Object> result = ImmutableMap.<String, Object>builder() ImmutableMap.Builder<String, Object> result = ImmutableMap.<String, Object>builder()
@ -323,4 +266,16 @@ public final class User {
return result.build(); return result.build();
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
usersCollection.insertOne(this, callback);
callback.get();
}
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
usersCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
} }

View File

@ -1,25 +1,49 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity;
import eu.dozd.mongo.annotation.Id;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Indexed;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Entity(value = "userMeta", noClassnameStored = true) @Entity
public final class UserMetaEntry { public final class UserMetaEntry {
private static final MongoCollection<UserMetaEntry> userMetaCollection = APIv3.getDatabase().getCollection("userMeta", UserMetaEntry.class);
@Getter @Id private String id; @Getter @Id private String id;
@Getter @Indexed private UUID user; @Getter private UUID user;
@Getter @Indexed private String serverGroup; @Getter private String serverGroup;
@Getter @Setter private Map<String, Object> data; @Getter @Setter private Map<String, Object> data;
public static List<UserMetaEntry> findAll() {
return SyncUtils.blockMulti(userMetaCollection.find());
}
public static UserMetaEntry findById(String id) {
return SyncUtils.blockOne(userMetaCollection.find(new Document("_id", id)));
}
public static UserMetaEntry findByUserAndGroup(User user, ServerGroup serverGroup) {
return findByUserAndGroup(user.getId(), serverGroup);
}
public static UserMetaEntry findByUserAndGroup(UUID user, ServerGroup serverGroup) {
return SyncUtils.blockOne(userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())));
}
public UserMetaEntry() {} // For Morphia public UserMetaEntry() {} // For Morphia
public UserMetaEntry(User user, ServerGroup serverGroup, Map<String, Object> data) { public UserMetaEntry(User user, ServerGroup serverGroup, Map<String, Object> data) {
@ -29,8 +53,22 @@ public final class UserMetaEntry {
this.data = ImmutableMap.copyOf(data); this.data = ImmutableMap.copyOf(data);
} }
public void insert() {
BlockingCallback<Void> callback = new BlockingCallback<>();
userMetaCollection.insertOne(this, callback);
callback.get();
}
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
userMetaCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
public void delete() { public void delete() {
APIv3.getDatastore().delete(this); BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
userMetaCollection.deleteOne(new Document("_id", id), callback);
callback.get();
} }
} }

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.routes; package net.frozenorb.apiv3.routes;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.Grant;
@ -28,7 +29,8 @@ public final class GETDump implements Handler<RoutingContext> {
List<UUID> banCache = new ArrayList<>(); List<UUID> banCache = new ArrayList<>();
List<UUID> blacklistCache = new ArrayList<>(); List<UUID> blacklistCache = new ArrayList<>();
APIv3.getDatastore().createQuery(Punishment.class).field("type").in(ImmutableSet.of( Punishment.findByType
(ImmutableSet.of(
Punishment.PunishmentType.BAN, Punishment.PunishmentType.BAN,
Punishment.PunishmentType.BLACKLIST Punishment.PunishmentType.BLACKLIST
)).forEach((punishment) -> { )).forEach((punishment) -> {
@ -50,7 +52,7 @@ public final class GETDump implements Handler<RoutingContext> {
if (tick == 0 || tick % 2 == 0) { if (tick == 0 || tick % 2 == 0) {
Map<String, List<UUID>> grantCache = new HashMap<>(); Map<String, List<UUID>> grantCache = new HashMap<>();
APIv3.getDatastore().createQuery(Grant.class).forEach((grant) -> { Grant.findAll().forEach((grant) -> {
if (grant.isActive()) { if (grant.isActive()) {
List<UUID> users = grantCache.get(grant.getRank()); List<UUID> users = grantCache.get(grant.getRank());
@ -88,21 +90,25 @@ public final class GETDump implements Handler<RoutingContext> {
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case "ban": case "ban":
return banCache; APIv3.respondJson(ctx, banCache);
return;
case "blacklist": case "blacklist":
return blacklistCache; APIv3.respondJson(ctx, blacklistCache);
return;
case "accessdeniable": // Lowercase d because we convert to lowercase above case "accessdeniable": // Lowercase d because we convert to lowercase above
List<UUID> result = new ArrayList<>(); List<UUID> result = new ArrayList<>();
result.addAll(banCache); result.addAll(banCache);
result.addAll(blacklistCache); result.addAll(blacklistCache);
return result; APIv3.respondJson(ctx, result);
return;
case "grant": case "grant":
return grantCache; APIv3.respondJson(ctx, grantCache);
return;
default: default:
ErrorUtils.respondInvalidInput(ctx, "type", type + " is not a valid type. Not in [ban, blacklist, accessDeniable, grant]"); ErrorUtils.respondInvalidInput(ctx, type + " is not a valid type. Not in [ban, blacklist, accessDeniable, grant]");
return return;
} }
} }

View File

@ -1,6 +1,9 @@
package net.frozenorb.apiv3.routes; package net.frozenorb.apiv3.routes;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.Actor;
public final class GETWhoAmI implements Handler<RoutingContext> { public final class GETWhoAmI implements Handler<RoutingContext> {
@ -8,11 +11,11 @@ public final class GETWhoAmI implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"name", actor.getName(), "name", actor.getName(),
"type", actor.getType(), "type", actor.getType(),
"authorized", actor.isAuthorized() "authorized", actor.isAuthorized()
); ));
} }
} }

View File

@ -1,17 +0,0 @@
package net.frozenorb.apiv3.routes;
import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Spark;
@Slf4j
public final class NotFound implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
log.info(req.requestMethod().toUpperCase() + " " + req.url());
Spark.halt(404, APIv3.getGson().toJson(ErrorUtils.respondNotFound("Route", req.url())));
return null;
}
}

View File

@ -1,13 +1,14 @@
package net.frozenorb.apiv3.routes; package net.frozenorb.apiv3.routes;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
public final class POSTMetrics implements Handler<RoutingContext> { public final class POSTMetrics implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
//LibratoBatch batch = new LibratoBatch(); APIv3.respondJson(ctx, ImmutableMap.of());
return ImmutableMap.of();
} }
} }

View File

@ -21,9 +21,9 @@ public final class GETAnnouncements implements Handler<RoutingContext> {
} }
Server sender = ((ServerActor) actor).getServer(); Server sender = ((ServerActor) actor).getServer();
ServerGroup senderGroup = ServerGroup.byId(sender.getServerGroup()); ServerGroup senderGroup = ServerGroup.findById(sender.getServerGroup());
APIv3.respond(ctx, senderGroup.getAnnouncements()); APIv3.respondJson(ctx, senderGroup.getAnnouncements());
} }
} }

View File

@ -13,7 +13,7 @@ public final class GETAuditLog implements Handler<RoutingContext> {
int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit")); int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit"));
int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset")); int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset"));
APIv3.respond(ctx, APIv3.getDatastore().createQuery(AuditLogEntry.class).order("performedAt").limit(limit).offset(offset).asList()); APIv3.respondJson(ctx, APIv3.getDatastore().createQuery(AuditLogEntry.class).order("performedAt").limit(limit).offset(offset).asList());
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
ErrorUtils.respondInvalidInput(ctx, "limit/offset must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "limit/offset must be numerical inputs.");
} }

View File

@ -1,7 +1,6 @@
package net.frozenorb.apiv3.routes.chatFilterList; package net.frozenorb.apiv3.routes.chatFilterList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
@ -9,7 +8,7 @@ import net.frozenorb.apiv3.APIv3;
public final class GETChatFilterList implements Handler<RoutingContext> { public final class GETChatFilterList implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respond(ctx, ImmutableMap.of()); APIv3.respondJson(ctx, ImmutableMap.of());
} }
} }

View File

@ -13,7 +13,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils;
public final class DELETEGrant implements Handler<RoutingContext> { public final class DELETEGrant implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Grant grant = Grant.byId(ctx.request().getParam("id")); Grant grant = Grant.findById(ctx.request().getParam("id"));
if (grant == null) { if (grant == null) {
ErrorUtils.respondNotFound(ctx, "Grant", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Grant", ctx.request().getParam("id"));
@ -23,7 +23,7 @@ public final class DELETEGrant implements Handler<RoutingContext> {
return; return;
} }
User removedBy = User.byId(ctx.request().getParam("removedBy")); User removedBy = User.findById(ctx.request().getParam("removedBy"));
if (removedBy == null) { if (removedBy == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy"));
@ -39,7 +39,7 @@ public final class DELETEGrant implements Handler<RoutingContext> {
grant.delete(removedBy, reason); grant.delete(removedBy, reason);
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of()); AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of());
APIv3.respond(ctx, grant); APIv3.respondJson(ctx, grant);
} }
} }

View File

@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.Grant;
public final class GETGrant implements Handler<RoutingContext> { public final class GETGrant implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respond(ctx, Grant.byId(ctx.request().getParam("id"))); APIv3.respondJson(ctx, Grant.findById(ctx.request().getParam("id")));
} }
} }

View File

@ -13,7 +13,7 @@ public final class GETGrants implements Handler<RoutingContext> {
int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit")); int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit"));
int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset")); int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset"));
APIv3.respond(ctx, APIv3.getDatastore().createQuery(Grant.class).order("addedAt").limit(limit).offset(offset).asList()); APIv3.respondJson(ctx, APIv3.getDatastore().createQuery(Grant.class).order("addedAt").limit(limit).offset(offset).asList());
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
ErrorUtils.respondInvalidInput(ctx, "limit and offset must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "limit and offset must be numerical inputs.");
} }

View File

@ -3,20 +3,21 @@ package net.frozenorb.apiv3.routes.grants;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class GETUserGrants implements Handler<RoutingContext> { public final class GETUserGrants implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.byId(ctx.request().getParam("id")); User target = User.findById(ctx.request().getParam("id"));
if (target == null) { if (target == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return; return;
} }
APIv3.respond(ctx, target.getGrants()); APIv3.respondJson(ctx, Grant.findByUser(target));
} }
} }

View File

@ -1,5 +1,7 @@
package net.frozenorb.apiv3.routes.grants; package net.frozenorb.apiv3.routes.grants;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.Rank; import net.frozenorb.apiv3.models.Rank;
@ -14,16 +16,18 @@ import java.util.Set;
public final class POSTUserGrant implements Handler<RoutingContext> { public final class POSTUserGrant implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.byId(ctx.request().getParam("id")); User target = User.findById(ctx.request().getParam("id"));
if (target == null) { if (target == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
String reason = ctx.request().getParam("reason"); String reason = ctx.request().getParam("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
return ErrorUtils.respondRequiredInput("reason"); ErrorUtils.respondRequiredInput(ctx, "reason");
return;
} }
Set<ServerGroup> scopes = new HashSet<>(); Set<ServerGroup> scopes = new HashSet<>();
@ -31,20 +35,22 @@ public final class POSTUserGrant implements Handler<RoutingContext> {
if (!scopesUnparsed.isEmpty()) { if (!scopesUnparsed.isEmpty()) {
for (String serverGroupId : scopesUnparsed.split(",")) { for (String serverGroupId : scopesUnparsed.split(",")) {
ServerGroup serverGroup = ServerGroup.byId(serverGroupId); ServerGroup serverGroup = ServerGroup.findById(serverGroupId);
if (serverGroup == null) { if (serverGroup == null) {
return ErrorUtils.respondNotFound("Server group", serverGroupId); ErrorUtils.respondNotFound(ctx, "Server group", serverGroupId);
return;
} }
scopes.add(serverGroup); scopes.add(serverGroup);
} }
} }
Rank rank = Rank.byId(ctx.request().getParam("rank")); Rank rank = Rank.findById(ctx.request().getParam("rank"));
if (rank == null) { if (rank == null) {
return ErrorUtils.respondNotFound("Rank", ctx.request().getParam("rank")); ErrorUtils.respondNotFound(ctx, "Rank", ctx.request().getParam("rank"));
return;
} }
Date expiresAt; Date expiresAt;
@ -56,15 +62,16 @@ public final class POSTUserGrant implements Handler<RoutingContext> {
} }
if (expiresAt != null && expiresAt.before(new Date())) { if (expiresAt != null && expiresAt.before(new Date())) {
return ErrorUtils.respondInvalidInput("Expiration date cannot be in the past."); ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past.");
return;
} }
// We purposely don't do a null check, grants don't have to have a source. // We purposely don't do a null check, grants don't have to have a source.
User addedBy = User.byId(ctx.request().getParam("addedBy")); User addedBy = User.findById(ctx.request().getParam("addedBy"));
Grant grant = new Grant(target, reason, scopes, rank, expiresAt, addedBy); Grant grant = new Grant(target, reason, scopes, rank, expiresAt, addedBy);
APIv3.getDatastore().save(grant); grant.insert();
return grant; APIv3.respondJson(ctx, grant);
} }
} }

View File

@ -3,20 +3,21 @@ package net.frozenorb.apiv3.routes.ipLog;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.IPLogEntry;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class GETUserIPLog implements Handler<RoutingContext> { public final class GETUserIPLog implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.byId(ctx.request().getParam("id")); User target = User.findById(ctx.request().getParam("id"));
if (target == null) { if (target == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return; return;
} }
APIv3.respond(ctx, target.getIPLog()); APIv3.respondJson(ctx, IPLogEntry.findByUser(target));
} }
} }

View File

@ -9,7 +9,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils;
public final class DELETENotificationTemplate implements Handler<RoutingContext> { public final class DELETENotificationTemplate implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
NotificationTemplate notificationTemplate = NotificationTemplate.byId(ctx.request().getParam("id")); NotificationTemplate notificationTemplate = NotificationTemplate.findById(ctx.request().getParam("id"));
if (notificationTemplate == null) { if (notificationTemplate == null) {
ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("id"));
@ -17,7 +17,7 @@ public final class DELETENotificationTemplate implements Handler<RoutingContext>
} }
notificationTemplate.delete(); notificationTemplate.delete();
APIv3.respond(ctx, notificationTemplate); APIv3.respondJson(ctx, notificationTemplate);
} }
} }

View File

@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.NotificationTemplate;
public final class GETNotificationTemplate implements Handler<RoutingContext> { public final class GETNotificationTemplate implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respond(ctx, NotificationTemplate.byId(ctx.request().getParam("id"))); APIv3.respondJson(ctx, NotificationTemplate.findById(ctx.request().getParam("id")));
} }
} }

View File

@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.NotificationTemplate;
public final class GETNotificationTemplates implements Handler<RoutingContext> { public final class GETNotificationTemplates implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respond(ctx, NotificationTemplate.values()); APIv3.respondJson(ctx, NotificationTemplate.findAll());
} }
} }

View File

@ -13,8 +13,8 @@ public final class POSTNotificationTemplate implements Handler<RoutingContext> {
String body = ctx.request().getParam("body"); String body = ctx.request().getParam("body");
NotificationTemplate notificationTemplate = new NotificationTemplate(id, subject, body); NotificationTemplate notificationTemplate = new NotificationTemplate(id, subject, body);
APIv3.getDatastore().save(notificationTemplate); notificationTemplate.insert();
APIv3.respond(ctx, notificationTemplate); APIv3.respondJson(ctx, notificationTemplate);
} }
} }

View File

@ -1,6 +1,9 @@
package net.frozenorb.apiv3.routes.punishments; package net.frozenorb.apiv3.routes.punishments;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.auditLog.AuditLogActionType;
import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.Punishment;
@ -10,29 +13,33 @@ import net.frozenorb.apiv3.utils.ErrorUtils;
public final class DELETEPunishment implements Handler<RoutingContext> { public final class DELETEPunishment implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Punishment punishment = Punishment.byId(ctx.request().getParam("id")); Punishment punishment = Punishment.findById(ctx.request().getParam("id"));
if (punishment == null) { if (punishment == null) {
return ErrorUtils.respondNotFound("Punishment", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Punishment", ctx.request().getParam("id"));
return;
} else if (!punishment.isActive()) { } else if (!punishment.isActive()) {
return ErrorUtils.respondInvalidInput("Cannot remove an inactive punishment."); ErrorUtils.respondInvalidInput(ctx, "Cannot remove an inactive punishment.");
return;
} }
User removedBy = User.byId(ctx.request().getParam("removedBy")); User removedBy = User.findById(ctx.request().getParam("removedBy"));
if (removedBy == null) { if (removedBy == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("removedBy")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy"));
return;
} }
String reason = ctx.request().getParam("reason"); String reason = ctx.request().getParam("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
return ErrorUtils.respondRequiredInput("reason"); ErrorUtils.respondRequiredInput(ctx, "reason");
return;
} }
punishment.delete(removedBy, reason); punishment.delete(removedBy, reason);
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of()); AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of());
return punishment; APIv3.respondJson(ctx, punishment);
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.punishments; package net.frozenorb.apiv3.routes.punishments;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.Punishment;
public final class GETPunishment implements Handler<RoutingContext> { public final class GETPunishment implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return Punishment.byId(ctx.request().getParam("id")); APIv3.respondJson(ctx, Punishment.findById(ctx.request().getParam("id")));
} }
} }

View File

@ -1,5 +1,7 @@
package net.frozenorb.apiv3.routes.punishments; package net.frozenorb.apiv3.routes.punishments;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
@ -11,9 +13,9 @@ public final class GETPunishments implements Handler<RoutingContext> {
int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit")); int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit"));
int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset")); int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset"));
return APIv3.getDatastore().createQuery(Punishment.class).order("addedAt").limit(limit).offset(offset).asList(); APIv3.respondJson(ctx, APIv3.getDatastore().createQuery(Punishment.class).order("addedAt").limit(limit).offset(offset).asList());
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
return ErrorUtils.respondInvalidInput("limit and offset must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "limit and offset must be numerical inputs.");
} }
} }

View File

@ -1,18 +1,23 @@
package net.frozenorb.apiv3.routes.punishments; package net.frozenorb.apiv3.routes.punishments;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class GETUserPunishments implements Handler<RoutingContext> { public final class GETUserPunishments implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.byId(ctx.request().getParam("id")); User target = User.findById(ctx.request().getParam("id"));
if (target == null) { if (target == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
return target.getPunishments(); APIv3.respondJson(ctx, Punishment.findByUser(target));
} }
} }

View File

@ -2,6 +2,8 @@ package net.frozenorb.apiv3.routes.punishments;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
@ -15,24 +17,27 @@ import java.util.Map;
public final class POSTUserPunish implements Handler<RoutingContext> { public final class POSTUserPunish implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.byId(ctx.request().getParam("id")); User target = User.findById(ctx.request().getParam("id"));
if (target == null) { if (target == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
String reason = ctx.request().getParam("reason"); String reason = ctx.request().getParam("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
return ErrorUtils.respondRequiredInput("reason"); ErrorUtils.respondRequiredInput(ctx, "reason");
return;
} }
Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type")); Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type"));
if (type != Punishment.PunishmentType.WARN) { if (type != Punishment.PunishmentType.WARN) {
for (Punishment punishment : target.getPunishments(ImmutableSet.of(type))) { for (Punishment punishment : Punishment.findByUserAndType(target, ImmutableSet.of(type))) {
if (punishment.isActive()) { if (punishment.isActive()) {
return ErrorUtils.error("A punishment by " + User.byId(punishment.getAddedBy()).getLastUsername() + " already covers this user."); ErrorUtils.respondGeneric(ctx, "A punishment by " + User.findById(punishment.getAddedBy()).getLastUsername() + " already covers this user.");
return;
} }
} }
} }
@ -46,30 +51,33 @@ public final class POSTUserPunish implements Handler<RoutingContext> {
} }
if (expiresAt != null && expiresAt.before(new Date())) { if (expiresAt != null && expiresAt.before(new Date())) {
return ErrorUtils.respondInvalidInput("Expiration date cannot be in the past."); ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past.");
return;
} }
Map<String, Object> meta = Document.parse(req.body()); Map<String, Object> meta = Document.parse(ctx.getBodyAsString());
if (meta == null) { if (meta == null) {
return ErrorUtils.respondRequiredInput("request body meta"); ErrorUtils.respondRequiredInput(ctx, "request body meta");
return;
} }
// We purposely don't do a null check, grants don't have to have a source. // We purposely don't do a null check, grants don't have to have a source.
User addedBy = User.byId(ctx.request().getParam("addedBy")); User addedBy = User.findById(ctx.request().getParam("addedBy"));
if (target.hasPermissionAnywhere(Permissions.PROTECTED_PUNISHMENT)) { if (target.hasPermissionAnywhere(Permissions.PROTECTED_PUNISHMENT)) {
return ErrorUtils.error(target.getLastSeenOn() + " is protected from punishments."); ErrorUtils.respondGeneric(ctx, target.getLastSeenOn() + " is protected from punishments.");
return;
} }
Punishment punishment = new Punishment(target, reason, type, expiresAt, addedBy, ctx.get("actor"), meta); Punishment punishment = new Punishment(target, reason, type, expiresAt, addedBy, ctx.get("actor"), meta);
String accessDenialReason = punishment.getAccessDenialReason(); String accessDenialReason = punishment.getAccessDenialReason();
APIv3.getDatastore().save(punishment); punishment.insert();
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"punishment", punishment, "punishment", punishment,
"accessDenialReason", accessDenialReason == null ? "" : accessDenialReason "accessDenialReason", accessDenialReason == null ? "" : accessDenialReason
); ));
} }
} }

View File

@ -1,19 +1,23 @@
package net.frozenorb.apiv3.routes.ranks; package net.frozenorb.apiv3.routes.ranks;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Rank; import net.frozenorb.apiv3.models.Rank;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class DELETERank implements Handler<RoutingContext> { public final class DELETERank implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Rank rank = Rank.byId(ctx.request().getParam("id")); Rank rank = Rank.findById(ctx.request().getParam("id"));
if (rank == null) { if (rank == null) {
return ErrorUtils.respondNotFound("Rank", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Rank", ctx.request().getParam("id"));
return;
} }
rank.delete(); rank.delete();
return rank; APIv3.respondJson(ctx, rank);
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.ranks; package net.frozenorb.apiv3.routes.ranks;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Rank; import net.frozenorb.apiv3.models.Rank;
public final class GETRank implements Handler<RoutingContext> { public final class GETRank implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return Rank.byId(ctx.request().getParam("id")); APIv3.respondJson(ctx, Rank.findById(ctx.request().getParam("id")));
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.ranks; package net.frozenorb.apiv3.routes.ranks;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Rank; import net.frozenorb.apiv3.models.Rank;
public final class GETRanks implements Handler<RoutingContext> { public final class GETRanks implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return Rank.values(); APIv3.respondJson(ctx, Rank.findAll());
} }
} }

View File

@ -1,5 +1,7 @@
package net.frozenorb.apiv3.routes.ranks; package net.frozenorb.apiv3.routes.ranks;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Rank; import net.frozenorb.apiv3.models.Rank;
@ -14,8 +16,8 @@ public final class POSTRank implements Handler<RoutingContext> {
boolean staffRank = Boolean.parseBoolean(ctx.request().getParam("staffRank")); boolean staffRank = Boolean.parseBoolean(ctx.request().getParam("staffRank"));
Rank rank = new Rank(id, weight, displayName, gameColor, websiteColor, staffRank); Rank rank = new Rank(id, weight, displayName, gameColor, websiteColor, staffRank);
APIv3.getDatastore().save(rank); rank.insert();
return rank; APIv3.respondJson(ctx, rank);
} }
} }

View File

@ -1,19 +1,23 @@
package net.frozenorb.apiv3.routes.serverGroups; package net.frozenorb.apiv3.routes.serverGroups;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class DELETEServerGroup implements Handler<RoutingContext> { public final class DELETEServerGroup implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
ServerGroup serverGroup = ServerGroup.byId(ctx.request().getParam("id")); ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("id"));
if (serverGroup == null) { if (serverGroup == null) {
return ErrorUtils.respondNotFound("Server group", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id"));
return;
} }
serverGroup.delete(); serverGroup.delete();
return serverGroup; APIv3.respondJson(ctx, serverGroup);
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.serverGroups; package net.frozenorb.apiv3.routes.serverGroups;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
public final class GETServerGroup implements Handler<RoutingContext> { public final class GETServerGroup implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return ServerGroup.byId(ctx.request().getParam("id")); APIv3.respondJson(ctx, ServerGroup.findById(ctx.request().getParam("id")));
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.serverGroups; package net.frozenorb.apiv3.routes.serverGroups;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
public final class GETServerGroups implements Handler<RoutingContext> { public final class GETServerGroups implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return ServerGroup.values(); APIv3.respondJson(ctx, ServerGroup.findAll());
} }
} }

View File

@ -1,5 +1,7 @@
package net.frozenorb.apiv3.routes.serverGroups; package net.frozenorb.apiv3.routes.serverGroups;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
@ -11,8 +13,8 @@ public final class POSTServerGroup implements Handler<RoutingContext> {
boolean isPublic = Boolean.valueOf(ctx.request().getParam("public")); boolean isPublic = Boolean.valueOf(ctx.request().getParam("public"));
ServerGroup serverGroup = new ServerGroup(id, image, isPublic); ServerGroup serverGroup = new ServerGroup(id, image, isPublic);
APIv3.getDatastore().save(serverGroup); serverGroup.insert();
return serverGroup; APIv3.respondJson(ctx, serverGroup);
} }
} }

View File

@ -1,19 +1,23 @@
package net.frozenorb.apiv3.routes.servers; package net.frozenorb.apiv3.routes.servers;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class DELETEServer implements Handler<RoutingContext> { public final class DELETEServer implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Server server = Server.byId(ctx.request().getParam("id")); Server server = Server.findById(ctx.request().getParam("id"));
if (server == null) { if (server == null) {
return ErrorUtils.respondNotFound("Server", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Server", ctx.request().getParam("id"));
return;
} }
server.delete(); server.delete();
return server; APIv3.respondJson(ctx, server);
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.servers; package net.frozenorb.apiv3.routes.servers;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
public final class GETServer implements Handler<RoutingContext> { public final class GETServer implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return Server.byId(ctx.request().getParam("id")); APIv3.respondJson(ctx, Server.findById(ctx.request().getParam("id")));
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.servers; package net.frozenorb.apiv3.routes.servers;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
public final class GETServers implements Handler<RoutingContext> { public final class GETServers implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return Server.values(); APIv3.respondJson(ctx, Server.findAll());
} }
} }

View File

@ -1,5 +1,7 @@
package net.frozenorb.apiv3.routes.servers; package net.frozenorb.apiv3.routes.servers;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
@ -12,20 +14,22 @@ public final class POSTServer implements Handler<RoutingContext> {
String id = ctx.request().getParam("id"); String id = ctx.request().getParam("id");
String displayName = ctx.request().getParam("displayName"); String displayName = ctx.request().getParam("displayName");
String apiKey = ctx.request().getParam("apiKey"); String apiKey = ctx.request().getParam("apiKey");
ServerGroup group = ServerGroup.byId(ctx.request().getParam("group")); ServerGroup group = ServerGroup.findById(ctx.request().getParam("group"));
String ip = ctx.request().getParam("ip"); String ip = ctx.request().getParam("ip");
if (group == null) { if (group == null) {
return ErrorUtils.respondNotFound("Server group", ctx.request().getParam("group")); ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("group"));
return;
} }
if (!IPUtils.isValidIP(ip)) { if (!IPUtils.isValidIP(ip)) {
return ErrorUtils.respondInvalidInput("IP address \"" + ip + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "IP address \"" + ip + "\" is not valid.");
return;
} }
Server server = new Server(id, displayName, apiKey, group, ip); Server server = new Server(id, displayName, apiKey, group, ip);
APIv3.getDatastore().save(server); server.insert();
return server; APIv3.respondJson(ctx, server);
} }
} }

View File

@ -1,6 +1,8 @@
package net.frozenorb.apiv3.routes.servers; package net.frozenorb.apiv3.routes.servers;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.Actor;
@ -21,16 +23,17 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");
if (actor.getType() != ActorType.SERVER) { if (actor.getType() != ActorType.SERVER) {
return ErrorUtils.respondServerOnly(); ErrorUtils.respondServerOnly(ctx);
return;
} }
Server actorServer = Server.byId(actor.getName()); Server actorServer = Server.byId(actor.getName());
ServerGroup actorServerGroup = ServerGroup.byId(actorServer.getServerGroup()); ServerGroup actorServerGroup = ServerGroup.byId(actorServer.getServerGroup());
Document reqJson = Document.parse(req.body()); Document reqJson = Document.parse(ctx.getBodyAsString());
Map<UUID, String> onlinePlayersNames = new HashMap<>(); Map<UUID, String> onlinePlayersNames = new HashMap<>();
Map<UUID, User> onlinePlayersUsers = new HashMap<>(); Map<UUID, User> onlinePlayersUsers;
Map<UUID, List<Grant>> onlinePlayersGrants = new HashMap<>(); Map<UUID, List<Grant>> onlinePlayersGrants;
Map<UUID, List<Punishment>> onlinePlayersPunishments = new HashMap<>(); Map<UUID, List<Punishment>> onlinePlayersPunishments;
Map<String, Object> playersResponse = new HashMap<>(); Map<String, Object> playersResponse = new HashMap<>();
// This code is messy, but we have to do db ops in parallel to avoid // This code is messy, but we have to do db ops in parallel to avoid
@ -71,7 +74,7 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
UUID uuid = entry.getKey(); UUID uuid = entry.getKey();
User user = entry.getValue(); User user = entry.getValue();
playersResponse.put(uuid.toString(), user.getLoginInfo(actorServer, onlinePlayersPunishments.get(uuid), onlinePlayersGrants.get(uuid))); playersResponse.put(uuid.toString(), user.prepareLoginInfo(actorServer, onlinePlayersPunishments.get(uuid), onlinePlayersGrants.get(uuid)));
} }
for (Object event : (List<Object>) reqJson.get("events")) { for (Object event : (List<Object>) reqJson.get("events")) {
@ -92,7 +95,7 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
Map<String, Map<String, Boolean>> permissionsResponse = new HashMap<>(); Map<String, Map<String, Boolean>> permissionsResponse = new HashMap<>();
for (Rank rank : Rank.values()) { for (Rank rank : Rank.findAll()) {
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions( Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
PermissionUtils.getDefaultPermissions(rank), PermissionUtils.getDefaultPermissions(rank),
actorServerGroup.calculatePermissions(rank) actorServerGroup.calculatePermissions(rank)
@ -104,12 +107,12 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
actorServer.setPlayers(onlinePlayersNames.keySet()); actorServer.setPlayers(onlinePlayersNames.keySet());
actorServer.setLastTps(reqJson.getDouble("lastTps")); actorServer.setLastTps(reqJson.getDouble("lastTps"));
actorServer.setLastUpdatedAt(new Date()); actorServer.setLastUpdatedAt(new Date());
APIv3.getDatastore().save(actorServer); actorServer.save();
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"players", playersResponse, "players", playersResponse,
"permissions", permissionsResponse "permissions", permissionsResponse
); ));
} }
} }

View File

@ -1,29 +1,39 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.models.UserMetaEntry; import net.frozenorb.apiv3.models.UserMetaEntry;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import org.bson.Document;
public final class DELETEUserMeta implements Handler<RoutingContext> { public final class DELETEUserMeta implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
ServerGroup serverGroup = ServerGroup.byId(ctx.request().getParam("serverGroup")); ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("serverGroup"));
if (serverGroup == null) { if (serverGroup == null) {
return ErrorUtils.respondNotFound("Server group", ctx.request().getParam("serverGroup")); ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("serverGroup"));
return;
} }
UserMetaEntry userMetaEntry = user.getMeta(serverGroup); UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroup(user, serverGroup);
userMetaEntry.delete(); if (userMetaEntry != null) {
return userMetaEntry.getData(); userMetaEntry.delete();
APIv3.respondJson(ctx, userMetaEntry.getData());
} else {
APIv3.respondJson(ctx, new Document());
}
} }
} }

View File

@ -2,6 +2,9 @@ package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.auditLog.AuditLogActionType;
import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.Punishment;
@ -11,34 +14,38 @@ import net.frozenorb.apiv3.utils.ErrorUtils;
public final class DELETEUserPunishment implements Handler<RoutingContext> { public final class DELETEUserPunishment implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.byId(ctx.request().getParam("id")); User target = User.findById(ctx.request().getParam("id"));
if (target == null) { if (target == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type").toUpperCase()); Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type").toUpperCase());
User removedBy = User.byId(ctx.request().getParam("removedBy")); User removedBy = User.findById(ctx.request().getParam("removedBy"));
if (removedBy == null) { if (removedBy == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("removedBy")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy"));
return;
} }
String reason = ctx.request().getParam("reason"); String reason = ctx.request().getParam("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
return ErrorUtils.respondRequiredInput("reason"); ErrorUtils.respondRequiredInput(ctx, "reason");
return;
} }
for (Punishment punishment : target.getPunishments(ImmutableSet.of(type))) { for (Punishment punishment : target.getPunishments(ImmutableSet.of(type))) {
if (punishment.isActive()) { if (punishment.isActive()) {
punishment.delete(removedBy, reason); punishment.delete(removedBy, reason);
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of()); AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of());
return punishment; APIv3.respondJson(ctx, punishment);
return;
} }
} }
return ErrorUtils.error("User provided has no active punishments"); ErrorUtils.respondGeneric(ctx, "User provided has no active punishments");
} }
} }

View File

@ -1,5 +1,7 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.Rank; import net.frozenorb.apiv3.models.Rank;
@ -12,22 +14,22 @@ public final class GETStaff implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Map<String, Rank> staffRanks = new HashMap<>(); Map<String, Rank> staffRanks = new HashMap<>();
Rank.values().forEach(rank -> { Rank.findAll().forEach(rank -> {
if (rank.isStaffRank()) { if (rank.isStaffRank()) {
staffRanks.put(rank.getId(), rank); staffRanks.put(rank.getId(), rank);
} }
}); });
Map<String, Set<User>> result = new TreeMap<>((first, second) -> { Map<String, Set<User>> result = new TreeMap<>((first, second) -> {
Rank firstRank = Rank.byId(first); Rank firstRank = staffRanks.get(first);
Rank secondRank = Rank.byId(second); Rank secondRank = staffRanks.get(second);
return Integer.compare(firstRank.getWeight(), secondRank.getWeight()); return Integer.compare(firstRank.getWeight(), secondRank.getWeight());
}); });
APIv3.getDatastore().createQuery(Grant.class).field("rank").in(staffRanks.keySet()).forEach(grant -> { Grant.findByRank(staffRanks.values()).forEach(grant -> {
if (grant.isActive()) { if (grant.isActive()) {
User user = User.byId(grant.getUser()); User user = User.findById(grant.getUser());
Rank rank = staffRanks.get(grant.getRank()); Rank rank = staffRanks.get(grant.getRank());
if (!result.containsKey(rank.getId())) { if (!result.containsKey(rank.getId())) {
@ -38,7 +40,7 @@ public final class GETStaff implements Handler<RoutingContext> {
} }
}); });
return result; APIv3.respondJson(ctx, result);
} }
} }

View File

@ -1,11 +1,14 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
public final class GETUser implements Handler<RoutingContext> { public final class GETUser implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
return User.byId(ctx.request().getParam("id")); APIv3.respondJson(ctx, User.findById(ctx.request().getParam("id")));
} }
} }

View File

@ -1,27 +1,35 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.IPLogEntry;
import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class GETUserDetails implements Handler<RoutingContext> { public final class GETUserDetails implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
// Too many fields to use .of() // Too many fields to use .of()
return ImmutableMap.builder() APIv3.respondJson(ctx, ImmutableMap.builder()
.put("user", user) .put("user", user)
.put("grants", user.getGrants()) .put("grants", Grant.findByUser(user))
.put("ipLog", user.getIPLog()) .put("ipLog", IPLogEntry.findByUser(user))
.put("punishments", user.getPunishments()) .put("punishments", Punishment.findByUser(user))
.put("aliases", user.getAliases()) .put("aliases", user.getAliases())
.put("totpSetup", user.getTotpSecret() != null) .put("totpSetup", user.getTotpSecret() != null)
.build(); .build()
);
} }
} }

View File

@ -1,5 +1,8 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.models.UserMetaEntry; import net.frozenorb.apiv3.models.UserMetaEntry;
@ -8,20 +11,22 @@ import net.frozenorb.apiv3.utils.ErrorUtils;
public final class GETUserMeta implements Handler<RoutingContext> { public final class GETUserMeta implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
ServerGroup serverGroup = ServerGroup.byId(ctx.request().getParam("serverGroup")); ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("serverGroup"));
if (serverGroup == null) { if (serverGroup == null) {
return ErrorUtils.respondNotFound("Server group", ctx.request().getParam("serverGroup")); ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("serverGroup"));
return;
} }
UserMetaEntry userMetaEntry = user.getMeta(serverGroup); UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroup(user, serverGroup);
return userMetaEntry.getData(); APIv3.respondJson(ctx, userMetaEntry.getData());
} }
} }

View File

@ -1,6 +1,9 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.IPUtils;
@ -9,36 +12,39 @@ import net.frozenorb.apiv3.utils.TOTPUtils;
public final class GETUserRequiresTOTP implements Handler<RoutingContext> { public final class GETUserRequiresTOTP implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
if (user.getTotpSecret() == null) { if (user.getTotpSecret() == null) {
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"required", false, "required", false,
"message", "User does not have TOTP setup." "message", "User does not have TOTP setup."
); ));
return;
} }
String userIp = ctx.request().getParam("userIp"); String userIp = ctx.request().getParam("userIp");
if (!IPUtils.isValidIP(userIp)) { if (!IPUtils.isValidIP(userIp)) {
return ErrorUtils.respondInvalidInput("IP address \"" + userIp + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid.");
return;
} }
if (TOTPUtils.isPreAuthorized(user, userIp)) { if (TOTPUtils.isPreAuthorized(user, userIp)) {
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"required", false, "required", false,
"message", "User's IP has already been validated" "message", "User's IP has already been validated"
); ));
} else {
APIv3.respondJson(ctx, ImmutableMap.of(
"required", true,
"message", "User has no TOTP exemptions."
));
} }
return ImmutableMap.of(
"required", true,
"message", "User has no TOTP exemptions."
);
} }
} }

View File

@ -1,27 +1,32 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
public final class GETUserVerifyPassword implements Handler<RoutingContext> { public final class GETUserVerifyPassword implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
if (user.getPassword() == null) { if (user.getPassword() == null) {
return ErrorUtils.respondInvalidInput("User provided does not have password set."); ErrorUtils.respondInvalidInput(ctx, "User provided does not have password set.");
return;
} }
boolean authorized = user.checkPassword(ctx.request().getParam("password")); boolean authorized = user.checkPassword(ctx.request().getParam("password"));
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", authorized "authorized", authorized
); ));
} }
} }

View File

@ -2,6 +2,8 @@ package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
@ -20,37 +22,42 @@ public final class POSTUserConfirmRegister implements Handler<RoutingContext> {
"nicole chelsea biteme matthew access yankees 987654321 dallas austin thunder taylor matrix").split(" ")); "nicole chelsea biteme matthew access yankees 987654321 dallas austin thunder taylor matrix").split(" "));
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byEmailToken(ctx.request().getParam("emailToken")); User user = User.findByEmailToken(ctx.request().getParam("emailToken"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("Email token", ctx.request().getParam("emailToken")); ErrorUtils.respondNotFound(ctx, "Email token", ctx.request().getParam("emailToken"));
return;
} }
// We can't check email != null as that's set while we're pending // We can't check email != null as that's set while we're pending
// confirmation, we have to check the token. // confirmation, we have to check the token.
if (user.getEmailToken() == null) { if (user.getEmailToken() == null) {
return ErrorUtils.error("User provided already has email set."); ErrorUtils.respondGeneric(ctx, "User provided already has email set.");
return;
} }
if ((System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) > TimeUnit.DAYS.toMillis(2)) { if ((System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) > TimeUnit.DAYS.toMillis(2)) {
return ErrorUtils.error("Email token is expired"); ErrorUtils.respondGeneric(ctx, "Email token is expired");
return;
} }
String password = ctx.request().getParam("password"); String password = ctx.request().getParam("password");
if (password.length() < 8) { if (password.length() < 8) {
return ErrorUtils.error("Your password is too short."); ErrorUtils.respondGeneric(ctx, "Your password is too short.");
return;
} else if (commonPasswords.contains(password)) { } else if (commonPasswords.contains(password)) {
return ErrorUtils.error("Your password is too common. Please use a more secure password."); ErrorUtils.respondGeneric(ctx, "Your password is too common. Please use a more secure password.");
return;
} }
user.setEmailToken(null); user.setEmailToken(null);
user.setPassword(password); user.setPassword(password);
APIv3.getDatastore().save(user); user.save();
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"success", true "success", true
); ));
} }
} }

View File

@ -1,24 +1,25 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Request;
import spark.Response;
public class POSTUserLeave implements Handler<RoutingContext> { public class POSTUserLeave implements Handler<RoutingContext> {
@Override @Override
public Object handle(Request req, Response res) throws Exception { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
user.leftServer(); user.leftServer();
APIv3.getDatastore().save(user); user.save();
return user; APIv3.respondJson(ctx, user);
} }
} }

View File

@ -1,5 +1,8 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.actors.ActorType; import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
@ -16,20 +19,23 @@ public final class POSTUserLogin implements Handler<RoutingContext> {
UUID uuid = UUID.fromString(ctx.request().getParam("id")); UUID uuid = UUID.fromString(ctx.request().getParam("id"));
if (!UUIDUtils.isAcceptableUUID(uuid)) { if (!UUIDUtils.isAcceptableUUID(uuid)) {
return ErrorUtils.respondInvalidInput("UUID \"" + uuid + "\" is not valid - must be version 4 UUID."); ErrorUtils.respondInvalidInput(ctx, "UUID \"" + uuid + "\" is not valid - must be version 4 UUID.");
return;
} }
User user = User.byId(uuid); User user = User.findById(uuid);
String username = ctx.request().getParam("username"); String username = ctx.request().getParam("username");
String userIp = ctx.request().getParam("userIp"); String userIp = ctx.request().getParam("userIp");
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");
if (actor.getType() != ActorType.SERVER) { if (actor.getType() != ActorType.SERVER) {
return ErrorUtils.respondServerOnly(); ErrorUtils.respondServerOnly(ctx);
return;
} }
if (!IPUtils.isValidIP(userIp)) { if (!IPUtils.isValidIP(userIp)) {
return ErrorUtils.respondInvalidInput("IP address \"" + userIp + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid.");
return;
} }
if (user == null) { if (user == null) {
@ -37,11 +43,11 @@ public final class POSTUserLogin implements Handler<RoutingContext> {
user = new User(uuid, username); user = new User(uuid, username);
} }
Server actorServer = Server.byId(actor.getName()); Server actorServer = Server.findById(actor.getName());
user.getIPLogEntry(userIp).used(); user.getIPLogEntry(userIp).used();
user.updateUsername(username); user.updateUsername(username);
return user.getLoginInfo(actorServer); APIv3.respondJson(ctx, user.getLoginInfo(actorServer));
} }
} }

View File

@ -1,6 +1,9 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.NotificationTemplate; import net.frozenorb.apiv3.models.NotificationTemplate;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.unsorted.Notification;
@ -12,43 +15,47 @@ import java.util.Map;
public final class POSTUserNotify implements Handler<RoutingContext> { public final class POSTUserNotify implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
if (user.getEmail() == null) { if (user.getEmail() == null) {
return ErrorUtils.error("User provided does not have email set."); ErrorUtils.respondGeneric(ctx, "User provided does not have email set.");
return;
} }
NotificationTemplate template = NotificationTemplate.byId(ctx.request().getParam("template")); NotificationTemplate template = NotificationTemplate.findById(ctx.request().getParam("template"));
if (template == null) { if (template == null) {
return ErrorUtils.respondNotFound("Notification template", ctx.request().getParam("template")); ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("template"));
return;
} }
Map<String, Object> subjectReplacements = new HashMap<>(); Map<String, Object> subjectReplacements = new HashMap<>();
Map<String, Object> bodyReplacements = new HashMap<>(); Map<String, Object> bodyReplacements = new HashMap<>();
req.queryMap("subject").toMap().forEach((key, values) -> { //TODO: Probably make this use the body as json
/*req.queryMap("subject").toMap().forEach((key, values) -> {
subjectReplacements.put(key, values[0]); subjectReplacements.put(key, values[0]);
}); });
req.queryMap("body").toMap().forEach((key, values) -> { req.queryMap("body").toMap().forEach((key, values) -> {
bodyReplacements.put(key, values[0]); bodyReplacements.put(key, values[0]);
}); });*/
try { try {
Notification notification = new Notification(template, subjectReplacements, bodyReplacements); Notification notification = new Notification(template, subjectReplacements, bodyReplacements);
notification.sendAsEmail(user.getEmail()); notification.sendAsEmail(user.getEmail());
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"success", true "success", true
); ));
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
return ErrorUtils.error("Failed to send notification"); ErrorUtils.respondGeneric(ctx, "Failed to send notification");
} }
} }

View File

@ -1,6 +1,8 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.NotificationTemplate; import net.frozenorb.apiv3.models.NotificationTemplate;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
@ -22,30 +24,34 @@ public final class POSTUserRegister implements Handler<RoutingContext> {
); );
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
if (user.getEmail() != null) { if (user.getEmail() != null) {
return ErrorUtils.respondInvalidInput("User provided already has email set."); ErrorUtils.respondInvalidInput(ctx, "User provided already has email set.");
return;
} }
String email = ctx.request().getParam("email"); String email = ctx.request().getParam("email");
if (!VALID_EMAIL_PATTERN.matcher(email).matches()) { if (!VALID_EMAIL_PATTERN.matcher(email).matches()) {
return ErrorUtils.error(email + " is not a valid email."); ErrorUtils.respondGeneric(ctx, email + " is not a valid email.");
return;
} }
if (user.getEmailToken() != null && (System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) < TimeUnit.DAYS.toMillis(2)) { if (user.getEmailToken() != null && (System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) < TimeUnit.DAYS.toMillis(2)) {
return ErrorUtils.error("We just recently sent you a confirmation email. Please wait before trying to register again."); ErrorUtils.respondGeneric(ctx, "We just recently sent you a confirmation email. Please wait before trying to register again.");
return;
} }
user.setEmail(email); user.setEmail(email);
user.setEmailToken(new BigInteger(130, new Random()).toString(32)); user.setEmailToken(new BigInteger(130, new Random()).toString(32));
user.setEmailTokenSetAt(new Date()); user.setEmailTokenSetAt(new Date());
APIv3.getDatastore().save(user); user.save();
Map<String, Object> replacements = ImmutableMap.of( Map<String, Object> replacements = ImmutableMap.of(
"username", user.getLastUsername(), "username", user.getLastUsername(),
@ -53,16 +59,16 @@ public final class POSTUserRegister implements Handler<RoutingContext> {
"emailToken", user.getEmailToken() "emailToken", user.getEmailToken()
); );
Notification notification = new Notification(NotificationTemplate.byId("email-confirmation"), replacements, replacements); Notification notification = new Notification(NotificationTemplate.findById("email-confirmation"), replacements, replacements);
try { try {
notification.sendAsEmail(user.getEmail()); notification.sendAsEmail(user.getEmail());
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"success", true "success", true
); ));
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
return ErrorUtils.error("Failed to send confirmation email. Please contact a MineHQ staff member."); ErrorUtils.respondGeneric(ctx, "Failed to send confirmation email. Please contact a MineHQ staff member.");
} }
} }

View File

@ -2,6 +2,8 @@ package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
@ -10,24 +12,26 @@ import net.frozenorb.apiv3.utils.TOTPUtils;
public final class POSTUserSetupTOTP implements Handler<RoutingContext> { public final class POSTUserSetupTOTP implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
if (user.getTotpSecret() != null) { if (user.getTotpSecret() != null) {
return ErrorUtils.respondInvalidInput("User provided already has TOTP code set."); ErrorUtils.respondInvalidInput(ctx, "User provided already has TOTP code set.");
return;
} }
GoogleAuthenticatorKey generated = TOTPUtils.generateTOTPKey(); GoogleAuthenticatorKey generated = TOTPUtils.generateTOTPKey();
user.setTotpSecret(generated.getKey()); user.setTotpSecret(generated.getKey());
APIv3.getDatastore().save(user); user.save();
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"qrCode", TOTPUtils.getQRCodeURL(user, generated) "qrCode", TOTPUtils.getQRCodeURL(user, generated)
); ));
} }
} }

View File

@ -1,6 +1,9 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.IPUtils;
@ -11,29 +14,33 @@ import java.util.concurrent.TimeUnit;
public final class POSTUserVerifyTOTP implements Handler<RoutingContext> { public final class POSTUserVerifyTOTP implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
if (user.getTotpSecret() == null) { if (user.getTotpSecret() == null) {
return ErrorUtils.respondInvalidInput("User provided does not have TOTP code set."); ErrorUtils.respondInvalidInput(ctx, "User provided does not have TOTP code set.");
return;
} }
String userIp = ctx.request().getParam("userIp"); String userIp = ctx.request().getParam("userIp");
if (!IPUtils.isValidIP(userIp)) { if (!IPUtils.isValidIP(userIp)) {
return ErrorUtils.respondInvalidInput("IP address \"" + userIp + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid.");
return;
} }
int providedCode = Integer.parseInt(ctx.request().getParam("code")); int providedCode = Integer.parseInt(ctx.request().getParam("code"));
if (TOTPUtils.wasRecentlyUsed(user, providedCode)) { if (TOTPUtils.wasRecentlyUsed(user, providedCode)) {
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", false, "authorized", false,
"message", "TOTP code was recently used." "message", "TOTP code was recently used."
); ));
return;
} }
boolean authorized = TOTPUtils.authorizeUser(user, providedCode); boolean authorized = TOTPUtils.authorizeUser(user, providedCode);
@ -42,15 +49,15 @@ public final class POSTUserVerifyTOTP implements Handler<RoutingContext> {
TOTPUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS); TOTPUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS);
TOTPUtils.markRecentlyUsed(user, providedCode); TOTPUtils.markRecentlyUsed(user, providedCode);
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", true, "authorized", true,
"message", "Valid TOTP code provided." "message", "Valid TOTP code provided."
); ));
} else { } else {
return ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", false, "authorized", false,
"message", "TOTP code was not valid." "message", "TOTP code was not valid."
); ));
} }
} }

View File

@ -1,5 +1,8 @@
package net.frozenorb.apiv3.routes.users; package net.frozenorb.apiv3.routes.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
@ -8,22 +11,24 @@ import org.bson.Document;
public final class PUTUserMeta implements Handler<RoutingContext> { public final class PUTUserMeta implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.byId(ctx.request().getParam("id")); User user = User.findById(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
return ErrorUtils.respondNotFound("User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
} }
ServerGroup serverGroup = ServerGroup.byId(ctx.request().getParam("serverGroup")); ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("serverGroup"));
if (serverGroup == null) { if (serverGroup == null) {
return ErrorUtils.respondNotFound("Server group", ctx.request().getParam("serverGroup")); ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("serverGroup"));
return;
} }
Document data = Document.parse(req.body()); Document data = Document.parse(ctx.getBodyAsString());
user.saveMeta(serverGroup, data); user.saveMeta(serverGroup, data);
return data; APIv3.respondJson(ctx, data);
} }
} }

View File

@ -0,0 +1,29 @@
package net.frozenorb.apiv3.unsorted;
import com.google.common.util.concurrent.SettableFuture;
import com.mongodb.async.SingleResultCallback;
import java.util.concurrent.ExecutionException;
public final class BlockingCallback<T> implements SingleResultCallback<T> {
private final SettableFuture<T> future = SettableFuture.create();
public void onResult(T val, Throwable error) {
if (error != null) {
future.setException(error);
} else {
future.set(val);
}
}
public T get() {
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
// No matter what we get we'll just rethrow.
throw new RuntimeException(ex);
}
}
}

View File

@ -1,27 +0,0 @@
package net.frozenorb.apiv3.unsorted;
import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.APIv3;
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) {
long started = req.attribute("requestStarted");
APIv3.getStatsD().recordExecutionTime("apiv3.http.executionTime", System.currentTimeMillis() - started);
APIv3.getStatsD().incrementCounter("apiv3.http.requests");
APIv3.getStatsD().incrementCounter("apiv3.http.errors");
String code = new ObjectId().toHexString();
log.error(code + ":", ex);
res.body(APIv3.getGson().toJson(ErrorUtils.error("An unknown error has occurred. Please contact an API developer with the code \"" + code + "\".")));
}
}

View File

@ -9,23 +9,24 @@ import net.frozenorb.apiv3.APIv3;
public class ErrorUtils { public class ErrorUtils {
public static void respondServerOnly(RoutingContext ctx) { public static void respondServerOnly(RoutingContext ctx) {
error(ctx, "This action can only be performed when requested by a server."); respondGeneric(ctx, "This action can only be performed when requested by a server.");
} }
public static void respondNotFound(RoutingContext ctx, String itemType, String id) { public static void respondNotFound(RoutingContext ctx, String itemType, String id) {
error(ctx, "Not found: " + itemType + " with id " + id + " cannot be found."); respondGeneric(ctx, "Not found: " + itemType + " with id " + id + " cannot be found.");
} }
public static void respondInvalidInput(RoutingContext ctx, String message) { public static void respondInvalidInput(RoutingContext ctx, String message) {
error(ctx, "Invalid input: " + message + "."); respondGeneric(ctx, "Invalid input: " + message + ".");
} }
public static void respondRequiredInput(RoutingContext ctx, String field) { public static void respondRequiredInput(RoutingContext ctx, String field) {
error(ctx, "Field \"" + field + "\" is required."); respondGeneric(ctx, "Field \"" + field + "\" is required.");
} }
public static void error(RoutingContext ctx, String message) { public static void respondGeneric(RoutingContext ctx, String message) {
APIv3.respond(ctx, ImmutableMap.of( // TODO: Proper status codes
APIv3.respondJson(ctx, 400, ImmutableMap.of(
"success", false, "success", false,
"message", message "message", message
)); ));

View File

@ -1,10 +1,9 @@
package net.frozenorb.apiv3.utils; package net.frozenorb.apiv3.utils;
import com.mongodb.async.SingleResultCallback;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import okhttp3.OkHttpClient; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import okhttp3.Request;
import okhttp3.Response;
import org.bson.Document; import org.bson.Document;
import java.util.UUID; import java.util.UUID;
@ -12,30 +11,32 @@ import java.util.UUID;
@UtilityClass @UtilityClass
public class MojangUtils { public class MojangUtils {
private static OkHttpClient okHttpClient = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();
public static String getName(UUID id) { public static String getName(UUID id) {
Request.Builder builder = new Request.Builder(); BlockingCallback<String> callback = new BlockingCallback<>();
getName(id, callback);
return callback.get();
}
builder.get(); public static void getName(UUID id, SingleResultCallback<String> callback) {
builder.url("https://sessionserver.mojang.com/session/minecraft/profile/" + id.toString().replace("-", "")); APIv3.getHttpClient().get("sessionserver.mojang.com", "session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> {
response.bodyHandler((body) -> {
Document resJson = Document.parse(body.toString());
String name = resJson.getString("name");
try { if (name == null) {
Response response = okHttpClient.newCall(builder.build()).execute(); APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.rateLimited");
Document resJson = Document.parse(response.body().string()); callback.onResult(null, new RuntimeException("Hit Mojang API rate limit: " + resJson.toJson()));
} else {
APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.success");
callback.onResult(name, null);
}
});
String name = resJson.getString("name"); response.exceptionHandler((error) -> {
APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.failure");
if (name == null) { callback.onResult(null, error);
throw new RuntimeException("Hit Mojang API rate limit: " + resJson.toJson()); });
} });
APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.success");
return name;
} catch (Exception ex) {
APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.failure");
throw new RuntimeException(ex);
}
} }
} }

View File

@ -25,7 +25,7 @@ public class PermissionUtils {
public static Map<String, Boolean> mergeUpTo(Map<String, List<String>> unmerged, Rank upTo) { public static Map<String, Boolean> mergeUpTo(Map<String, List<String>> unmerged, Rank upTo) {
Map<String, Boolean> result = new HashMap<>(); Map<String, Boolean> result = new HashMap<>();
for (Rank rank : Rank.values()) { for (Rank rank : Rank.findAll()) {
Map<String, Boolean> rankPermissions = convertToMap(unmerged.get(rank.getId())); Map<String, Boolean> rankPermissions = convertToMap(unmerged.get(rank.getId()));
// If there's no permissions defined for this rank just skip it. // If there's no permissions defined for this rank just skip it.

View File

@ -0,0 +1,27 @@
package net.frozenorb.apiv3.utils;
import com.mongodb.async.client.MongoIterable;
import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import java.util.ArrayList;
import java.util.List;
@UtilityClass
public class SyncUtils {
public static <T> T blockOne(MongoIterable<T> mongoIterable) {
BlockingCallback<T> callback = new BlockingCallback<>();
mongoIterable.first(callback);
return callback.get();
}
public static <T> List<T> blockMulti(MongoIterable<T> mongoIterable) {
BlockingCallback<List<T>> callback = new BlockingCallback<>();
mongoIterable.into(new ArrayList<>(), callback);
return callback.get();
}
}