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

This commit is contained in:
Colin McDonald 2016-06-12 16:36:11 -04:00
parent 60a622fc43
commit 7e462e1915
65 changed files with 1026 additions and 520 deletions

View File

@ -1,6 +1,4 @@
general.releaseStage=production general.releaseStage=production
logging.level=info
logging.debug=true
mongo.address=localhost mongo.address=localhost
mongo.port=27017 mongo.port=27017
mongo.database=MineHQ mongo.database=MineHQ
@ -13,6 +11,5 @@ twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740
twillio.authToken=982592505a171d3be6b0722f5ecacc0e twillio.authToken=982592505a171d3be6b0722f5ecacc0e
mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ
bugsnag.apiKey=0e47fba8b825416b7cbc839066184509 bugsnag.apiKey=0e47fba8b825416b7cbc839066184509
auth.permittedUserRanks=developer,owner
auth.websiteApiKey=RVbp4hY6sCFVaf auth.websiteApiKey=RVbp4hY6sCFVaf
auth.bungeeCordApiKey=6d9cf76dc9f0d23 auth.bungeeCordApiKey=6d9cf76dc9f0d23

27
pom.xml
View File

@ -55,6 +55,10 @@
<id>minehq-repo</id> <id>minehq-repo</id>
<url>http://maven.minehq.com:8081/artifactory/minehq-all/</url> <url>http://maven.minehq.com:8081/artifactory/minehq-all/</url>
</repository> </repository>
<repository>
<id>mongo-jackson-codec-repo</id>
<url>https://dl.bintray.com/ylemoigne/maven</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -62,12 +66,17 @@
<dependency> <dependency>
<groupId>io.vertx</groupId> <groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId> <artifactId>vertx-core</artifactId>
<version>LATEST</version> <version>3.2.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.vertx</groupId> <groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId> <artifactId>vertx-web</artifactId>
<version>LATEST</version> <version>3.2.1</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-redis-client</artifactId>
<version>3.2.1</version>
</dependency> </dependency>
<!-- Google Libs --> <!-- Google Libs -->
@ -89,16 +98,14 @@
<version>3.0.4</version> <version>3.0.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>eu.dozd</groupId> <groupId>fr.javatic.mongo</groupId>
<artifactId>mongo-mapper</artifactId> <artifactId>mongo-jackson-codec</artifactId>
<version>1.0.1</version> <version>3.2.0__0.4</version>
</dependency> </dependency>
<!-- Redis -->
<dependency> <dependency>
<groupId>redis.clients</groupId> <groupId>de.undercouch</groupId>
<artifactId>jedis</artifactId> <artifactId>bson4jackson</artifactId>
<version>2.8.1</version> <version>2.6.0</version>
</dependency> </dependency>
<!-- Notifications --> <!-- Notifications -->

View File

@ -1,39 +1,50 @@
package net.frozenorb.apiv3; package net.frozenorb.apiv3;
import com.bugsnag.Client; import com.bugsnag.Client;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
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 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.Block; import com.mongodb.Block;
import com.mongodb.ConnectionString;
import com.mongodb.MongoCredential; import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress; import com.mongodb.ServerAddress;
import com.mongodb.async.client.MongoClient; import com.mongodb.async.client.MongoClient;
import com.mongodb.async.client.MongoClientSettings; import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.async.client.MongoClients; import com.mongodb.async.client.MongoClients;
import com.mongodb.async.client.MongoDatabase; import com.mongodb.async.client.MongoDatabase;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ClusterSettings; 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 fr.javatic.mongo.jacksonCodec.JacksonCodecProvider;
import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpServer; 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.BodyHandler;
import io.vertx.ext.web.handler.LoggerHandler; import io.vertx.ext.web.handler.LoggerHandler;
import io.vertx.ext.web.impl.BlockingHandlerDecorator;
import io.vertx.ext.web.impl.RouteImpl;
import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions;
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.ActorAttributeFilter; import net.frozenorb.apiv3.handlers.ActorAttributeHandler;
import net.frozenorb.apiv3.filters.AuthorizationFilter; import net.frozenorb.apiv3.handlers.AuthorizationHandler;
import net.frozenorb.apiv3.filters.MetricsHandler; import net.frozenorb.apiv3.handlers.MetricsHandler;
import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.*;
import net.frozenorb.apiv3.models.IPLogEntry;
import net.frozenorb.apiv3.models.Punishment;
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.POSTMetrics; import net.frozenorb.apiv3.routes.POSTMetrics;
@ -57,19 +68,23 @@ import net.frozenorb.apiv3.routes.serverGroups.GETServerGroups;
import net.frozenorb.apiv3.routes.serverGroups.POSTServerGroup; import net.frozenorb.apiv3.routes.serverGroups.POSTServerGroup;
import net.frozenorb.apiv3.routes.servers.*; import net.frozenorb.apiv3.routes.servers.*;
import net.frozenorb.apiv3.routes.users.*; import net.frozenorb.apiv3.routes.users.*;
import net.frozenorb.apiv3.serialization.DateTypeAdapter; import net.frozenorb.apiv3.serialization.*;
import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter;
import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger; import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger;
import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.IPUtils;
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.bson.codecs.BsonValueCodecProvider;
import org.bson.codecs.DocumentCodecProvider;
import org.bson.codecs.ValueCodecProvider;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistries; import org.bson.codecs.configuration.CodecRegistries;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import redis.clients.jedis.JedisPool;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*; 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;
@ -80,26 +95,27 @@ public final class APIv3 extends AbstractVerticle {
@Getter private static HttpClient httpClient; @Getter private static HttpClient httpClient;
@Getter private static MongoDatabase database; @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 RedisClient redisClient;
@Getter private static StatsDClient statsD; @Getter private static StatsDClient statsD;
@Getter private static Vertx vertxInstance;
@Getter private static final Gson gson = new GsonBuilder() @Getter private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter())
.registerTypeAdapter(Date.class, new DateTypeAdapter()) .registerTypeAdapter(Date.class, new DateTypeAdapter())
.setExclusionStrategies(new FollowAnnotationExclusionStrategy()) .setExclusionStrategies(new FollowAnnotationExclusionStrategy())
.create(); .create();
@Override @Override
public void start() { public void start() {
setupConfig(); vertxInstance = vertx;
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", config.getProperty("logging.level"));
setupConfig();
setupDatabase(); setupDatabase();
setupRedis(); setupRedis();
setupMetrics(); setupMetrics();
setupBugsnag(); setupBugsnag();
setupHttpServer(); //setupHttpServer();
setupHttpClient();
//convertData("158.69.126.126", true); convertData("mongodb://158.69.126.126", true);
} }
private void setupConfig() { private void setupConfig() {
@ -125,18 +141,37 @@ public final class APIv3 extends AbstractVerticle {
ClusterSettings clusterSettings = ClusterSettings ClusterSettings clusterSettings = ClusterSettings
.builder() .builder()
.hosts(ImmutableList.of( .applyConnectionString(new ConnectionString("mongodb://" + config.getProperty("mongo.address") + ":" + config.getProperty("mongo.port")))
new ServerAddress(
config.getProperty("mongo.address"),
Integer.parseInt(config.getProperty("mongo.port"))
)
))
.build(); .build();
List<CodecProvider> providers = new ArrayList<>();
// Our override codec
providers.add(new MineHQCodecProvider());
// Normal providers
providers.add(new ValueCodecProvider());
providers.add(new DocumentCodecProvider());
providers.add(new BsonValueCodecProvider());
ObjectMapper objectMapper = ObjectMapperFactory.createObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(UUID.class, new UUIDJsonSerializer());
simpleModule.addDeserializer(UUID.class, new UUIDJsonDeserializer());
objectMapper.registerModule(simpleModule);
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
providers.add(new JacksonCodecProvider(objectMapper));
MongoClientSettings settings = MongoClientSettings MongoClientSettings settings = MongoClientSettings
.builder() .builder()
.codecRegistry(CodecRegistries.fromProviders(MongoMapper.getProviders())) .codecRegistry(CodecRegistries.fromProviders(providers))
.credentialList(credentials) .credentialList(credentials)
.clusterSettings(clusterSettings).build(); .clusterSettings(clusterSettings)
.build();
MongoClient client = MongoClients.create(settings); MongoClient client = MongoClients.create(settings);
database = client.getDatabase(config.getProperty("mongo.database")); database = client.getDatabase(config.getProperty("mongo.database"));
@ -144,9 +179,11 @@ public final class APIv3 extends AbstractVerticle {
} }
private void setupRedis() { private void setupRedis() {
redisPool = new JedisPool( redisClient = RedisClient.create(
config.getProperty("redis.address"), vertx,
Integer.parseInt(config.getProperty("redis.port")) new RedisOptions()
.setAddress(config.getProperty("redis.address"))
.setPort(Integer.parseInt(config.getProperty("redis.port")))
); );
} }
@ -176,8 +213,8 @@ public final class APIv3 extends AbstractVerticle {
Router mainRouter = Router.router(vertx); Router mainRouter = Router.router(vertx);
mainRouter.route().handler(new MetricsHandler()); mainRouter.route().handler(new MetricsHandler());
mainRouter.route().handler(new ActorAttributeFilter()); mainRouter.route().handler(new ActorAttributeHandler());
mainRouter.route().handler(new AuthorizationFilter()); mainRouter.route().handler(new AuthorizationHandler());
mainRouter.route().handler(LoggerHandler.create()); mainRouter.route().handler(LoggerHandler.create());
mainRouter.route().handler(BodyHandler.create()); mainRouter.route().handler(BodyHandler.create());
@ -218,7 +255,7 @@ public final class APIv3 extends AbstractVerticle {
mainRouter.get("/server/:id").blockingHandler(new GETServer()); mainRouter.get("/server/:id").blockingHandler(new GETServer());
mainRouter.get("/servers").blockingHandler(new GETServers()); mainRouter.get("/servers").blockingHandler(new GETServers());
mainRouter.post("/server/heartbeat").blockingHandler(new POSTServerHeartbeat()); mainRouter.post("/server/heartbeat").handler(new POSTServerHeartbeat());
mainRouter.post("/server").blockingHandler(new POSTServer()); mainRouter.post("/server").blockingHandler(new POSTServer());
//put("/server/:id").blockingHandler(new PUTServer()); //put("/server/:id").blockingHandler(new PUTServer());
mainRouter.delete("/server/:id").blockingHandler(new DELETEServer()); mainRouter.delete("/server/:id").blockingHandler(new DELETEServer());
@ -246,7 +283,22 @@ public final class APIv3 extends AbstractVerticle {
mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment()); mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment());
mainRouter.getRoutes().forEach((route) -> { mainRouter.getRoutes().forEach((route) -> {
System.out.println(route.getClass() + "||" + route.getPath()); try {
RouteImpl impl = (RouteImpl) route;
Field field = RouteImpl.class.getDeclaredField("contextHandler");
field.setAccessible(true);
Handler<RoutingContext> handler = (Handler<RoutingContext>) field.get(impl);
if (handler instanceof BlockingHandlerDecorator) {
field = BlockingHandlerDecorator.class.getDeclaredField("decoratedHandler");
field.setAccessible(true);
handler = (Handler<RoutingContext>) field.get(handler);
}
System.out.println(route.getPath() + " is handled by " + handler.getClass());
} catch (Exception ex) {
}
}); });
int port = Integer.parseInt(config.getProperty("http.port")); int port = Integer.parseInt(config.getProperty("http.port"));
@ -276,10 +328,7 @@ public final class APIv3 extends AbstractVerticle {
AtomicInteger skippedGrants = new AtomicInteger(); AtomicInteger skippedGrants = new AtomicInteger();
AtomicInteger skippedIpLogs = new AtomicInteger(); AtomicInteger skippedIpLogs = new AtomicInteger();
importFrom.getCollection("user").find().forEach(new Block<Document>() { SyncUtils.blockMulti(importFrom.getCollection("user").find()).forEach((user) -> {
@Override
public void apply(Document user) {
String uuidString = String.valueOf(user.get("uuid")); String uuidString = String.valueOf(user.get("uuid"));
if (uuidString == null || uuidString.length() != 32) { if (uuidString == null || uuidString.length() != 32) {
@ -298,7 +347,7 @@ public final class APIv3 extends AbstractVerticle {
User created = new User( User created = new User(
uuid, uuid,
String.valueOf(user.get("name")).toString(), String.valueOf(user.get("name")).toString(),
ImmutableMap.of(), ImmutableMap.of(user.getString("name"), user.getDate("joined")),
null, null,
null, null,
null, null,
@ -316,14 +365,10 @@ public final class APIv3 extends AbstractVerticle {
} }
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>() { SyncUtils.blockMulti(importFrom.getCollection("punishment").find()).forEach((punishment) -> {
UUID target = mongoIdToUUID.get(((Map<String, Object>) punishment.get("user")).get("$id"));
@Override
public void apply(Document punishment) {
UUID target = mongoIdToUUID.get(((DBRef) punishment.get("user")).getId());
if (target == null) { if (target == null) {
skippedPunishments.incrementAndGet(); skippedPunishments.incrementAndGet();
@ -335,8 +380,6 @@ public final class APIv3 extends AbstractVerticle {
return; return;
} }
com.mongodb.
Punishment created = new Punishment( Punishment created = new Punishment(
new ObjectId().toString(), new ObjectId().toString(),
target, target,
@ -344,11 +387,11 @@ public final class APIv3 extends AbstractVerticle {
Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()), Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()),
punishment.getDate("expires"), punishment.getDate("expires"),
punishment.containsKey("meta") ? (punishment.get("meta") instanceof List ? ImmutableMap.of() : (Document) punishment.get("meta")) : ImmutableMap.of(), punishment.containsKey("meta") ? (punishment.get("meta") instanceof List ? ImmutableMap.of() : (Document) punishment.get("meta")) : ImmutableMap.of(),
punishment.containsKey("addedBy") ? mongoIdToUUID.get(((DBRef) punishment.get("addedBy")).getId()) : null, punishment.containsKey("addedBy") ? mongoIdToUUID.get(((Map<String, Object>) punishment.get("addedBy")).get("$id")) : null,
(Date) punishment.getDate("created").clone(), (Date) punishment.getDate("created").clone(),
punishment.containsKey("createdOn") ? String.valueOf(((DBRef) punishment.get("createdOn")).getId()) : "Website", punishment.containsKey("createdOn") ? String.valueOf(((Map<String, Object>) punishment.get("createdOn")).get("$id")) : "Website",
punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE, punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE,
punishment.containsKey("removedBy") ? (((DBRef) punishment.get("removedBy")).getCollectionName().equals("user") ? mongoIdToUUID.get(((DBRef) punishment.get("removedBy")).getId()) : null) : null, punishment.containsKey("removedBy") ? (((Map<String, Object>) punishment.get("removedBy")).get("$ref").equals("user") ? mongoIdToUUID.get(((Map<String, Object>) punishment.get("removedBy")).get("$id")) : null) : null,
punishment.getDate("created"), punishment.getDate("created"),
punishment.containsKey("removalReason") ? punishment.getString("removalReason").toString() : "" punishment.containsKey("removalReason") ? punishment.getString("removalReason").toString() : ""
); );
@ -358,14 +401,10 @@ public final class APIv3 extends AbstractVerticle {
} }
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>() { SyncUtils.blockMulti(importFrom.getCollection("grant").find()).forEach((grant) -> {
UUID target = mongoIdToUUID.get(((Map<String, Object>) grant.get("target")).get("$id"));
@Override
public void apply(Document grant) {
UUID target = mongoIdToUUID.get(((DBRef) grant.get("target")).getId());
if (target == null) { if (target == null) {
skippedGrants.incrementAndGet(); skippedGrants.incrementAndGet();
@ -389,7 +428,7 @@ public final class APIv3 extends AbstractVerticle {
grant.containsKey("scope") ? ImmutableSet.copyOf((Collection<String>) grant.get("scope")) : ImmutableSet.of(), grant.containsKey("scope") ? ImmutableSet.copyOf((Collection<String>) grant.get("scope")) : ImmutableSet.of(),
rank, rank,
grant.getDate("expires"), grant.getDate("expires"),
grant.containsKey("addedBy") ? mongoIdToUUID.get(((DBRef) grant.get("addedBy")).getId()) : null, grant.containsKey("addedBy") ? mongoIdToUUID.get(((Map<String, Object>) grant.get("addedBy")).get("$id")) : null,
grant.containsKey("created") ? grant.getDate("created") : new Date(), grant.containsKey("created") ? grant.getDate("created") : new Date(),
null, null,
null, null,
@ -401,14 +440,10 @@ public final class APIv3 extends AbstractVerticle {
} }
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>() { SyncUtils.blockMulti(importFrom.getCollection("iplog").find()).forEach((ipLogEntry) -> {
UUID user = mongoIdToUUID.get(((Map<String, Object>) ipLogEntry.get("user")).get("$id"));
@Override
public void apply(Document ipLogEntry) {
UUID user = mongoIdToUUID.get(((DBRef) ipLogEntry.get("user")).getId());
if (user == null || ipLogEntry.getString("ip") == null) { if (user == null || ipLogEntry.getString("ip") == null) {
skippedIpLogs.incrementAndGet(); skippedIpLogs.incrementAndGet();
@ -442,8 +477,7 @@ public final class APIv3 extends AbstractVerticle {
} }
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

@ -5,6 +5,8 @@ import io.vertx.core.Vertx;
final class Main { final class Main {
public static void main(String[] args) { public static void main(String[] args) {
System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory");
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
Vertx.vertx().deployVerticle(new APIv3()); Vertx.vertx().deployVerticle(new APIv3());
} }

View File

@ -2,14 +2,17 @@ package net.frozenorb.apiv3.actors;
public final class BungeeCordActor implements Actor { public final class BungeeCordActor implements Actor {
@Override
public boolean isAuthorized() { public boolean isAuthorized() {
return true; return true;
} }
@Override
public String getName() { public String getName() {
return "BungeeCord"; return "BungeeCord";
} }
@Override
public ActorType getType() { public ActorType getType() {
return ActorType.BUNGEECORD; return ActorType.BUNGEECORD;
} }

View File

@ -1,5 +1,6 @@
package net.frozenorb.apiv3.actors; package net.frozenorb.apiv3.actors;
import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.Server;
@ -11,14 +12,17 @@ public final class ServerActor implements Actor {
this.server = server; this.server = server;
} }
@Override
public boolean isAuthorized() { public boolean isAuthorized() {
return true; return true;
} }
@Override
public String getName() { public String getName() {
return server.getId(); return server.getId();
} }
@Override
public ActorType getType() { public ActorType getType() {
return ActorType.SERVER; return ActorType.SERVER;
} }

View File

@ -2,14 +2,17 @@ package net.frozenorb.apiv3.actors;
public final class UnknownActor implements Actor { public final class UnknownActor implements Actor {
@Override
public boolean isAuthorized() { public boolean isAuthorized() {
return false; return false;
} }
@Override
public String getName() { public String getName() {
return "Unknown"; return "Unknown";
} }
@Override
public ActorType getType() { public ActorType getType() {
return ActorType.UNKNOWN; return ActorType.UNKNOWN;
} }

View File

@ -4,35 +4,40 @@ import com.google.common.collect.ImmutableSet;
import lombok.Getter; import lombok.Getter;
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.unsorted.Permissions;
import java.util.Set; import java.util.Set;
public final class UserActor implements Actor { public final class UserActor implements Actor {
private static final Set<String> permittedUserRanks = ImmutableSet.copyOf(APIv3.getConfig().getProperty("auth.permittedUserRanks").toLowerCase().split(","));
@Getter private final User user; @Getter private final User user;
// We use Boolean here so we can have null = not calculated; // We use Boolean here so we can have null = not calculated;
// Currently having this cached isn't important as we only check
// this once, but later on when we have non-logged in routes
// this will be important.
private Boolean cachedAuthorized = null; private Boolean cachedAuthorized = null;
public UserActor(User user) { public UserActor(User user) {
this.user = user; this.user = user;
} }
@Override
public boolean isAuthorized() { public boolean isAuthorized() {
if (cachedAuthorized != null) { if (cachedAuthorized != null) {
return cachedAuthorized; return cachedAuthorized;
} else { } else {
String highestRankId = user.getHighestRankAnywhere().getId(); boolean authorized = user.hasPermissionAnywhere(Permissions.SIGN_API_REQUEST);
cachedAuthorized = permittedUserRanks.contains(highestRankId.toLowerCase()); cachedAuthorized = authorized;
return cachedAuthorized; return authorized;
} }
} }
@Override
public String getName() { public String getName() {
return user.getLastUsername(); return user.getLastUsername();
} }
@Override
public ActorType getType() { public ActorType getType() {
return ActorType.USER; return ActorType.USER;
} }

View File

@ -2,14 +2,17 @@ package net.frozenorb.apiv3.actors;
public final class WebsiteActor implements Actor { public final class WebsiteActor implements Actor {
@Override
public boolean isAuthorized() { public boolean isAuthorized() {
return true; return true;
} }
@Override
public String getName() { public String getName() {
return "Website"; return "Website";
} }
@Override
public ActorType getType() { public ActorType getType() {
return ActorType.WEBSITE; return ActorType.WEBSITE;
} }

View File

@ -1,4 +1,4 @@
package net.frozenorb.apiv3.filters; package net.frozenorb.apiv3.handlers;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
@ -10,30 +10,30 @@ import net.frozenorb.apiv3.utils.ErrorUtils;
import java.util.Base64; import java.util.Base64;
public final class ActorAttributeFilter implements Handler<RoutingContext> { public final class ActorAttributeHandler implements Handler<RoutingContext> {
// TODO: MAKE THIS ASYNC @Override
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
String authHeader = ctx.request().getHeader("Authorization"); String authorizationHeader = ctx.request().getHeader("Authorization");
String mhqAuthHeader = ctx.request().getHeader("MHQ-Authorization"); String mhqAuthorizationHeader = ctx.request().getHeader("MHQ-Authorization");
if (authHeader != null) { if (authorizationHeader != null) {
processBasicAuthorization(authHeader, ctx); processBasicAuthorization(authorizationHeader, ctx);
} else if (mhqAuthHeader != null) { } else if (mhqAuthorizationHeader != null) {
processMHQAuthorization(mhqAuthHeader, ctx); processMHQAuthorization(mhqAuthorizationHeader, ctx);
} else { } else {
ctx.put("actor", new UnknownActor()); processNoAuthorization(ctx);
ctx.next();
} }
} }
@SuppressWarnings("deprecation") // We purposely get the User by their last username.
private void processBasicAuthorization(String authHeader, RoutingContext ctx) { 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 decodedHeader = new String(Base64.getDecoder().decode(encodedHeader.getBytes()));
String[] credentials = decodedHeader.split(":");
if (credentials.length == 2) { if (credentials.length == 2) {
User user = User.findByLastUsername(credentials[0]); User.findByLastUsername(credentials[0], (user, error) -> {
if (error != null) {
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)) {
@ -45,6 +45,11 @@ public final class ActorAttributeFilter implements Handler<RoutingContext> {
ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\""); ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\"");
ErrorUtils.respondGeneric(ctx, "Failed to authorize as " + credentials[0] + "."); ErrorUtils.respondGeneric(ctx, "Failed to authorize as " + credentials[0] + ".");
});
} else {
ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\"");
ErrorUtils.respondGeneric(ctx, "Failed to authorize as " + credentials[0] + ".");
}
} }
private void processMHQAuthorization(String authHeader, RoutingContext ctx) { private void processMHQAuthorization(String authHeader, RoutingContext ctx) {
@ -93,4 +98,9 @@ public final class ActorAttributeFilter implements Handler<RoutingContext> {
ErrorUtils.respondGeneric(ctx, "Failed to authorize."); ErrorUtils.respondGeneric(ctx, "Failed to authorize.");
} }
public void processNoAuthorization(RoutingContext ctx) {
ctx.put("actor", new UnknownActor());
ctx.next();
}
} }

View File

@ -1,4 +1,4 @@
package net.frozenorb.apiv3.filters; package net.frozenorb.apiv3.handlers;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
@ -6,8 +6,9 @@ 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;
public final class AuthorizationFilter implements Handler<RoutingContext> { public final class AuthorizationHandler implements Handler<RoutingContext> {
@Override
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");

View File

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

View File

@ -1,9 +1,9 @@
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.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
import eu.dozd.mongo.annotation.Id;
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;
@ -15,12 +15,8 @@ import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.util.Date; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Entity
public final class AuditLogEntry { public final class AuditLogEntry {
private static final MongoCollection<AuditLogEntry> auditLogCollection = APIv3.getDatabase().getCollection("auditLog", AuditLogEntry.class); private static final MongoCollection<AuditLogEntry> auditLogCollection = APIv3.getDatabase().getCollection("auditLog", AuditLogEntry.class);
@ -34,22 +30,46 @@ public final class AuditLogEntry {
@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() { public static List<AuditLogEntry> findAllSync() {
return SyncUtils.blockMulti(auditLogCollection.find()); return SyncUtils.blockMulti(auditLogCollection.find());
} }
public static AuditLogEntry findById(String id) { public static List<AuditLogEntry> findAllPaginatedSync(int skip, int pageSize) {
return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", 1)).skip(skip).limit(pageSize));
}
public static AuditLogEntry findByIdSync(String id) {
return SyncUtils.blockOne(auditLogCollection.find(new Document("_id", id))); return SyncUtils.blockOne(auditLogCollection.find(new Document("_id", id)));
} }
public static List<AuditLogEntry> findByUser(User user) { public static List<AuditLogEntry> findByUserSync(User user) {
return findByUser(user.getId()); return findByUserSync(user.getId());
} }
public static List<AuditLogEntry> findByUser(UUID user) { public static List<AuditLogEntry> findByUserSync(UUID user) {
return SyncUtils.blockMulti(auditLogCollection.find(new Document("user", user))); return SyncUtils.blockMulti(auditLogCollection.find(new Document("user", user)));
} }
public static void findAll(SingleResultCallback<List<AuditLogEntry>> callback) {
auditLogCollection.find().into(new ArrayList<>(), callback);
}
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<AuditLogEntry>> callback) {
auditLogCollection.find().sort(new Document("performedAt", 1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<AuditLogEntry> callback) {
auditLogCollection.find(new Document("_id", id)).first(callback);
}
public static void findByUser(User user, SingleResultCallback<List<AuditLogEntry>> callback) {
findByUser(user.getId(), callback);
}
public static void findByUser(UUID user, SingleResultCallback<List<AuditLogEntry>> callback) {
auditLogCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
}
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) {

View File

@ -1,11 +1,10 @@
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.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import fr.javatic.mongo.jacksonCodec.objectId.Id;
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;
@ -16,7 +15,6 @@ import org.bson.types.ObjectId;
import java.util.*; import java.util.*;
@Entity
@AllArgsConstructor @AllArgsConstructor
public final class Grant { public final class Grant {
@ -36,26 +34,74 @@ public final class Grant {
@Getter private Date removedAt; @Getter private Date removedAt;
@Getter private String removalReason; @Getter private String removalReason;
public static List<Grant> findAll() { public static List<Grant> findAllSync() {
return SyncUtils.blockMulti(grantsCollection.find()); return SyncUtils.blockMulti(grantsCollection.find());
} }
public static List<Grant> findByRank(Iterable<Rank> ranks) { public static List<Grant> findAllPaginatedSync(int skip, int pageSize) {
return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize));
}
public static List<Grant> findByRankSync(Iterable<Rank> ranks) {
return SyncUtils.blockMulti(grantsCollection.find(new Document("rank", new Document("$in", ranks)))); return SyncUtils.blockMulti(grantsCollection.find(new Document("rank", new Document("$in", ranks))));
} }
public static Grant findById(String id) { public static Grant findByIdSync(String id) {
return SyncUtils.blockOne(grantsCollection.find(new Document("_id", id))); return SyncUtils.blockOne(grantsCollection.find(new Document("_id", id)));
} }
public static List<Grant> findByUser(User user) { public static List<Grant> findByUserSync(User user) {
return findByUser(user.getId()); return findByUserSync(user.getId());
} }
public static List<Grant> findByUser(UUID user) { public static List<Grant> findByUserSync(UUID user) {
return SyncUtils.blockMulti(grantsCollection.find(new Document("user", user))); return SyncUtils.blockMulti(grantsCollection.find(new Document("user", user)));
} }
public static void findAll(SingleResultCallback<List<Grant>> callback) {
grantsCollection.find().into(new ArrayList<>(), callback);
}
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Grant>> callback) {
grantsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
}
public static void findByRank(Iterable<Rank> ranks, SingleResultCallback<List<Grant>> callback) {
grantsCollection.find(new Document("rank", new Document("$in", ranks))).into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<Grant> callback) {
grantsCollection.find(new Document("_id", id)).first(callback);
}
public static void findByUser(User user, SingleResultCallback<List<Grant>> callback) {
findByUser(user.getId(), callback);
}
public static void findByUser(UUID user, SingleResultCallback<List<Grant>> callback) {
grantsCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
}
public static void findByUserGrouped(Iterable<UUID> users, SingleResultCallback<Map<UUID, List<Grant>>> callback) {
grantsCollection.find(new Document("user", new Document("$in", users))).into(new ArrayList<>(), (grants, error) -> {
if (error != null) {
callback.onResult(null, error);
} else {
Map<UUID, List<Grant>> result = new HashMap<>();
for (UUID user : users) {
result.put(user, new ArrayList<>());
}
for (Grant grant : grants) {
result.get(grant.getUser()).add(grant);
}
callback.onResult(result, null);
}
});
}
public Grant() {} // For Morphia public Grant() {} // For Morphia
public Grant(User user, String reason, Set<ServerGroup> scopes, Rank rank, Date expiresAt, User addedBy) { public Grant(User user, String reason, Set<ServerGroup> scopes, Rank rank, Date expiresAt, User addedBy) {

View File

@ -1,8 +1,9 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import eu.dozd.mongo.annotation.Entity; import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Id; import fr.javatic.mongo.jacksonCodec.objectId.Id;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
@ -11,11 +12,11 @@ import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@Entity
@AllArgsConstructor @AllArgsConstructor
public final class IPLogEntry { public final class IPLogEntry {
@ -28,30 +29,54 @@ public final class IPLogEntry {
@Getter private Date lastSeenAt; @Getter private Date lastSeenAt;
@Getter private int uses; @Getter private int uses;
public static List<IPLogEntry> findAll() { public static List<IPLogEntry> findAllSync() {
return SyncUtils.blockMulti(ipLogCollection.find()); return SyncUtils.blockMulti(ipLogCollection.find());
} }
public static IPLogEntry findById(String id) { public static IPLogEntry findByIdSync(String id) {
return SyncUtils.blockOne(ipLogCollection.find(new Document("_id", id))); return SyncUtils.blockOne(ipLogCollection.find(new Document("_id", id)));
} }
public static List<IPLogEntry> findByUser(User user) { public static List<IPLogEntry> findByUserSync(User user) {
return findByUser(user.getId()); return findByUserSync(user.getId());
} }
public static List<IPLogEntry> findByUser(UUID user) { public static List<IPLogEntry> findByUserSync(UUID user) {
return SyncUtils.blockMulti(ipLogCollection.find(new Document("user", user))); return SyncUtils.blockMulti(ipLogCollection.find(new Document("user", user)));
} }
public static IPLogEntry findByUserAndIp(User user, String userIp) { public static IPLogEntry findByUserAndIpSync(User user, String userIp) {
return findByUserAndIp(user.getId(), userIp); return findByUserAndIpSync(user.getId(), userIp);
} }
public static IPLogEntry findByUserAndIp(UUID user, String userIp) { public static IPLogEntry findByUserAndIpSync(UUID user, String userIp) {
return SyncUtils.blockOne(ipLogCollection.find(new Document("user", user).append("userIp", userIp))); return SyncUtils.blockOne(ipLogCollection.find(new Document("user", user).append("userIp", userIp)));
} }
public static void findAll(SingleResultCallback<List<IPLogEntry>> callback) {
ipLogCollection.find().into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<IPLogEntry> callback) {
ipLogCollection.find(new Document("_id", id)).first(callback);
}
public static void findByUser(User user, SingleResultCallback<List<IPLogEntry>> callback) {
findByUser(user.getId(), callback);
}
public static void findByUser(UUID user, SingleResultCallback<List<IPLogEntry>> callback) {
ipLogCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
}
public static void findByUserAndIp(User user, String userIp, SingleResultCallback<IPLogEntry> callback) {
findByUserAndIp(user.getId(), userIp, callback);
}
public static void findByUserAndIp(UUID user, String userIp, SingleResultCallback<IPLogEntry> callback) {
ipLogCollection.find(new Document("user", user).append("userIp", userIp)).first(callback);
}
public IPLogEntry() {} // For Morphia public IPLogEntry() {} // For Morphia
public IPLogEntry(User user, String userIp) { public IPLogEntry(User user, String userIp) {
@ -74,4 +99,10 @@ public final class IPLogEntry {
callback.get(); callback.get();
} }
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
ipLogCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
} }

View File

@ -1,21 +1,22 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
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.unsorted.BlockingCallback;
import net.frozenorb.apiv3.unsorted.Notification;
import net.frozenorb.apiv3.utils.SyncUtils; import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document; import org.bson.Document;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Entity
public final class NotificationTemplate { public final class NotificationTemplate {
private static final MongoCollection<NotificationTemplate> notificationTemplatesCollection = APIv3.getDatabase().getCollection("notificationTemplates", NotificationTemplate.class); private static final MongoCollection<NotificationTemplate> notificationTemplatesCollection = APIv3.getDatabase().getCollection("notificationTemplates", NotificationTemplate.class);
@ -24,14 +25,22 @@ public final class NotificationTemplate {
@Getter @Setter private String subject; @Getter @Setter private String subject;
@Getter @Setter private String body; @Getter @Setter private String body;
public static List<NotificationTemplate> findAll() { public static List<NotificationTemplate> findAllSync() {
return SyncUtils.blockMulti(notificationTemplatesCollection.find()); return SyncUtils.blockMulti(notificationTemplatesCollection.find());
} }
public static NotificationTemplate findById(String id) { public static NotificationTemplate findByIdSync(String id) {
return SyncUtils.blockOne(notificationTemplatesCollection.find(new Document("_id", id))); return SyncUtils.blockOne(notificationTemplatesCollection.find(new Document("_id", id)));
} }
public static void findAll(SingleResultCallback<List<NotificationTemplate>> callback) {
notificationTemplatesCollection.find().into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<NotificationTemplate> callback) {
notificationTemplatesCollection.find(new Document("_id", id)).first(callback);
}
public NotificationTemplate() {} // For Morphia public NotificationTemplate() {} // For Morphia
public NotificationTemplate(String id, String subject, String body) { public NotificationTemplate(String id, String subject, String body) {

View File

@ -1,9 +1,9 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
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;
@ -17,7 +17,6 @@ import org.bson.types.ObjectId;
import java.util.*; import java.util.*;
@Entity
@AllArgsConstructor @AllArgsConstructor
public final class Punishment { public final class Punishment {
@ -39,34 +38,90 @@ public final class Punishment {
@Getter private Date removedAt; @Getter private Date removedAt;
@Getter private String removalReason; @Getter private String removalReason;
public static List<Punishment> findAll() { public static List<Punishment> findAllSync() {
return SyncUtils.blockMulti(punishmentsCollection.find()); return SyncUtils.blockMulti(punishmentsCollection.find());
} }
public static List<Punishment> findByType(Iterable<PunishmentType> types) { public static List<Punishment> findAllPaginatedSync(int skip, int pageSize) {
return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize));
}
public static List<Punishment> findByTypeSync(Iterable<PunishmentType> types) {
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("type", new Document("$in", types)))); return SyncUtils.blockMulti(punishmentsCollection.find(new Document("type", new Document("$in", types))));
} }
public static Punishment findById(String id) { public static Punishment findByIdSync(String id) {
return SyncUtils.blockOne(punishmentsCollection.find(new Document("_id", id))); return SyncUtils.blockOne(punishmentsCollection.find(new Document("_id", id)));
} }
public static List<Punishment> findByUser(User user) { public static List<Punishment> findByUserSync(User user) {
return findByUser(user.getId()); return findByUserSync(user.getId());
} }
public static List<Punishment> findByUser(UUID user) { public static List<Punishment> findByUserSync(UUID user) {
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user))); return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user)));
} }
public static List<Punishment> findByUserAndType(User user, Iterable<PunishmentType> types) { public static List<Punishment> findByUserAndTypeSync(User user, Iterable<PunishmentType> types) {
return findByUserAndType(user.getId(), types); return findByUserAndTypeSync(user.getId(), types);
} }
public static List<Punishment> findByUserAndType(UUID user, Iterable<PunishmentType> types) { public static List<Punishment> findByUserAndTypeSync(UUID user, Iterable<PunishmentType> types) {
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", types)))); return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", types))));
} }
public static void findAll(SingleResultCallback<List<Punishment>> callback) {
punishmentsCollection.find().into(new ArrayList<>(), callback);
}
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Punishment>> callback) {
punishmentsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
}
public static void findByType(Iterable<PunishmentType> types, SingleResultCallback<List<Punishment>> callback) {
punishmentsCollection.find(new Document("type", new Document("$in", types))).into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<Punishment> callback) {
punishmentsCollection.find(new Document("_id", id)).first(callback);
}
public static void findByUser(User user, SingleResultCallback<List<Punishment>> callback) {
findByUser(user.getId(), callback);
}
public static void findByUser(UUID user, SingleResultCallback<List<Punishment>> callback) {
punishmentsCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
}
public static void findByUserGrouped(Iterable<UUID> users, SingleResultCallback<Map<UUID, List<Punishment>>> callback) {
punishmentsCollection.find(new Document("user", new Document("$in", users))).into(new ArrayList<>(), (punishments, error) -> {
if (error != null) {
callback.onResult(null, error);
} else {
Map<UUID, List<Punishment>> result = new HashMap<>();
for (UUID user : users) {
result.put(user, new ArrayList<>());
}
for (Punishment punishment : punishments) {
result.get(punishment.getUser()).add(punishment);
}
callback.onResult(result, null);
}
});
}
public static void findByUserAndType(User user, Iterable<PunishmentType> types, SingleResultCallback<List<Punishment>> callback) {
findByUserAndType(user.getId(), types, callback);
}
public static void findByUserAndType(UUID user, Iterable<PunishmentType> types, SingleResultCallback<List<Punishment>> callback) {
punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", types))).into(new ArrayList<>(), callback);
}
public Punishment() {} // For Morphia public Punishment() {} // For Morphia
public Punishment(User user, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map<String, Object> metadata) { public Punishment(User user, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map<String, Object> metadata) {

View File

@ -5,8 +5,7 @@ import com.google.common.primitives.Ints;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
import eu.dozd.mongo.annotation.Id;
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.unsorted.BlockingCallback;
@ -19,7 +18,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Entity
public final class Rank { public final class Rank {
private static final MongoCollection<Rank> ranksCollection = APIv3.getDatabase().getCollection("ranks", Rank.class); private static final MongoCollection<Rank> ranksCollection = APIv3.getDatabase().getCollection("ranks", Rank.class);

View File

@ -4,8 +4,7 @@ import com.google.common.collect.ImmutableSet;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
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;
@ -17,7 +16,6 @@ import org.bson.Document;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Entity
public final class Server { public final class Server {
private static final MongoCollection<Server> serversCollection = APIv3.getDatabase().getCollection("servers", Server.class); private static final MongoCollection<Server> serversCollection = APIv3.getDatabase().getCollection("servers", Server.class);

View File

@ -4,8 +4,7 @@ import com.google.gson.annotations.SerializedName;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
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;
@ -18,7 +17,6 @@ import org.bson.Document;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Entity
public final class ServerGroup { public final class ServerGroup {
private static final MongoCollection<ServerGroup> serverGroupsCollection = APIv3.getDatabase().getCollection("serverGroups", ServerGroup.class); private static final MongoCollection<ServerGroup> serverGroupsCollection = APIv3.getDatabase().getCollection("serverGroups", ServerGroup.class);
@ -29,8 +27,6 @@ public final class ServerGroup {
@Getter @Id private String id; @Getter @Id private String id;
@Getter private String image; @Getter private String image;
// We rename this to public (only to gson), we just can't name it that because it's a Java identifier.
@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<>();
@ -48,10 +44,9 @@ public final class ServerGroup {
public ServerGroup() {} // For Morphia public ServerGroup() {} // For Morphia
public ServerGroup(String id, String image, boolean isPublic) { public ServerGroup(String id, String image) {
this.id = id; this.id = id;
this.image = image; this.image = image;
this.isPublic = isPublic;
} }
public Map<String, Boolean> calculatePermissions(Rank userRank) { public Map<String, Boolean> calculatePermissions(Rank userRank) {

View File

@ -1,18 +1,22 @@
package net.frozenorb.apiv3.models; package net.frozenorb.apiv3.models;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Charsets; 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.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
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.serialization.UUIDJsonDeserializer;
import net.frozenorb.apiv3.serialization.UUIDJsonSerializer;
import net.frozenorb.apiv3.unsorted.BlockingCallback; 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;
@ -22,13 +26,12 @@ import org.bson.Document;
import java.util.*; import java.util.*;
@Entity
@AllArgsConstructor @AllArgsConstructor
public final class User { public final class User {
private static final MongoCollection<User> usersCollection = APIv3.getDatabase().getCollection("users", User.class); private static final MongoCollection<User> usersCollection = APIv3.getDatabase().getCollection("users", User.class);
@Getter @Id private UUID id; @Getter @Id @JsonSerialize(using=UUIDJsonSerializer.class) @JsonDeserialize(using=UUIDJsonDeserializer.class) private UUID id;
@Getter 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 @ExcludeFromReplies @Setter private String totpSecret; @Getter @ExcludeFromReplies @Setter private String totpSecret;
@ -42,11 +45,11 @@ public final class User {
@Getter private Date firstSeenAt; @Getter private Date firstSeenAt;
@Getter private boolean online; @Getter private boolean online;
public static List<User> findAll() { public static List<User> findAllSync() {
return SyncUtils.blockMulti(usersCollection.find()); return SyncUtils.blockMulti(usersCollection.find());
} }
public static User findById(String id) { public static User findByIdSync(String id) {
UUID uuid; UUID uuid;
try { try {
@ -55,10 +58,10 @@ public final class User {
return null; return null;
} }
return findById(uuid); return findByIdSync(uuid);
} }
public static User findById(UUID id) { public static User findByIdSync(UUID id) {
if (UUIDUtils.isAcceptableUUID(id)) { if (UUIDUtils.isAcceptableUUID(id)) {
return SyncUtils.blockOne(usersCollection.find(new Document("_id", id))); return SyncUtils.blockOne(usersCollection.find(new Document("_id", id)));
} else { } else {
@ -66,14 +69,63 @@ public final class User {
} }
} }
public static User findByEmailToken(String emailToken) { public static User findByEmailTokenSync(String emailToken) {
return SyncUtils.blockOne(usersCollection.find(new Document("emailToken", emailToken))); return SyncUtils.blockOne(usersCollection.find(new Document("emailToken", emailToken)));
} }
public static User findByLastUsername(String lastUsername) { public static User findByLastUsernameSync(String lastUsername) {
return SyncUtils.blockOne(usersCollection.find(new Document("lastUsername", lastUsername))); return SyncUtils.blockOne(usersCollection.find(new Document("lastUsername", lastUsername)));
} }
public static void findAll(SingleResultCallback<List<User>> callback) {
usersCollection.find().into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<User> callback) {
try {
UUID uuid = UUID.fromString(id);
findById(uuid, callback);
} catch (IllegalArgumentException ex) { // from UUID parsing
callback.onResult(null, ex);
}
}
public static void findById(UUID id, SingleResultCallback<User> callback) {
if (UUIDUtils.isAcceptableUUID(id)) {
usersCollection.find(new Document("_id", id)).first(callback);
} else {
callback.onResult(null, null);
}
}
public static void findByIdGrouped(Iterable<UUID> search, SingleResultCallback<Map<UUID, User>> callback) {
usersCollection.find(new Document("_id", new Document("$in", search))).into(new ArrayList<>(), (users, error) -> {
if (error != null) {
callback.onResult(null, error);
} else {
Map<UUID, User> result = new HashMap<>();
for (UUID user : search) {
result.put(user, null);
}
for (User user : users) {
result.put(user.getId(), user);
}
callback.onResult(result, null);
}
});
}
public static void findByEmailToken(String emailToken, SingleResultCallback<User> callback) {
usersCollection.find(new Document("emailToken", emailToken)).first(callback);
}
public static void findByLastUsername(String lastUsername, SingleResultCallback<User> callback) {
usersCollection.find(new Document("lastUsername", lastUsername)).first(callback);
}
public User() {} // For Morphia public User() {} // For Morphia
public User(UUID id, String lastUsername) { public User(UUID id, String lastUsername) {
@ -134,8 +186,10 @@ public final class User {
User withNewUsername; User withNewUsername;
while ((withNewUsername = User.findByLastUsername(username)) != null) { while ((withNewUsername = User.findByLastUsernameSync(username)) != null) {
String newUsername = MojangUtils.getName(withNewUsername.getId()); BlockingCallback<String> callback = new BlockingCallback<>();
MojangUtils.getName(withNewUsername.getId(), callback);
String newUsername = callback.get();
withNewUsername.updateUsername(newUsername); withNewUsername.updateUsername(newUsername);
} }
} }
@ -164,7 +218,7 @@ public final class User {
} }
public Rank getHighestRankScoped(ServerGroup serverGroup) { public Rank getHighestRankScoped(ServerGroup serverGroup) {
return getHighestRankScoped(serverGroup, Grant.findByUser(this)); return getHighestRankScoped(serverGroup, Grant.findByUserSync(this));
} }
// TODO: Clean // TODO: Clean
@ -195,7 +249,7 @@ public final class User {
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.findById("default"); Rank defaultRank = Rank.findById("default");
List<Grant> userGrants = Grant.findByUser(this); List<Grant> userGrants = Grant.findByUserSync(this);
for (ServerGroup serverGroup : ServerGroup.findAll()) { for (ServerGroup serverGroup : ServerGroup.findAll()) {
Rank highest = defaultRank; Rank highest = defaultRank;
@ -221,12 +275,12 @@ public final class User {
public Map<String, Object> getLoginInfo(Server server) { public Map<String, Object> getLoginInfo(Server server) {
return createLoginInfo( return createLoginInfo(
server, server,
Punishment.findByUserAndType(this, ImmutableSet.of( Punishment.findByUserAndTypeSync(this, ImmutableSet.of(
Punishment.PunishmentType.BLACKLIST, Punishment.PunishmentType.BLACKLIST,
Punishment.PunishmentType.BAN, Punishment.PunishmentType.BAN,
Punishment.PunishmentType.MUTE Punishment.PunishmentType.MUTE
)), )),
Grant.findByUser(this) Grant.findByUserSync(this)
); );
} }

View File

@ -1,11 +1,11 @@
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.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import eu.dozd.mongo.annotation.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id;
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;
@ -14,11 +14,11 @@ import net.frozenorb.apiv3.utils.SyncUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Entity
public final class UserMetaEntry { public final class UserMetaEntry {
private static final MongoCollection<UserMetaEntry> userMetaCollection = APIv3.getDatabase().getCollection("userMeta", UserMetaEntry.class); private static final MongoCollection<UserMetaEntry> userMetaCollection = APIv3.getDatabase().getCollection("userMeta", UserMetaEntry.class);
@ -28,22 +28,38 @@ public final class UserMetaEntry {
@Getter 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() { public static List<UserMetaEntry> findAllSync() {
return SyncUtils.blockMulti(userMetaCollection.find()); return SyncUtils.blockMulti(userMetaCollection.find());
} }
public static UserMetaEntry findById(String id) { public static UserMetaEntry findByIdSync(String id) {
return SyncUtils.blockOne(userMetaCollection.find(new Document("_id", id))); return SyncUtils.blockOne(userMetaCollection.find(new Document("_id", id)));
} }
public static UserMetaEntry findByUserAndGroup(User user, ServerGroup serverGroup) { public static UserMetaEntry findByUserAndGroupSync(User user, ServerGroup serverGroup) {
return findByUserAndGroup(user.getId(), serverGroup); return findByUserAndGroupSync(user.getId(), serverGroup);
} }
public static UserMetaEntry findByUserAndGroup(UUID user, ServerGroup serverGroup) { public static UserMetaEntry findByUserAndGroupSync(UUID user, ServerGroup serverGroup) {
return SyncUtils.blockOne(userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId()))); return SyncUtils.blockOne(userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())));
} }
public static void findAll(SingleResultCallback<List<UserMetaEntry>> callback) {
userMetaCollection.find().into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<UserMetaEntry> callback) {
userMetaCollection.find(new Document("_id", id)).first(callback);
}
public static void findByUserAndGroup(User user, ServerGroup serverGroup, SingleResultCallback<UserMetaEntry> callback) {
findByUserAndGroup(user.getId(), serverGroup, callback);
}
public static void findByUserAndGroup(UUID user, ServerGroup serverGroup, SingleResultCallback<UserMetaEntry> callback) {
userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())).first(callback);
}
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) {

View File

@ -29,8 +29,7 @@ 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<>();
Punishment.findByType Punishment.findByTypeSync(ImmutableSet.of(
(ImmutableSet.of(
Punishment.PunishmentType.BAN, Punishment.PunishmentType.BAN,
Punishment.PunishmentType.BLACKLIST Punishment.PunishmentType.BLACKLIST
)).forEach((punishment) -> { )).forEach((punishment) -> {
@ -52,7 +51,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<>();
Grant.findAll().forEach((grant) -> { Grant.findAllSync().forEach((grant) -> {
if (grant.isActive()) { if (grant.isActive()) {
List<UUID> users = grantCache.get(grant.getRank()); List<UUID> users = grantCache.get(grant.getRank());

View File

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

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.findById(ctx.request().getParam("id")); Grant grant = Grant.findByIdSync(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.findById(ctx.request().getParam("removedBy")); User removedBy = User.findByIdSync(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"));

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.respondJson(ctx, Grant.findById(ctx.request().getParam("id"))); APIv3.respondJson(ctx, Grant.findByIdSync(ctx.request().getParam("id")));
} }
} }

View File

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

View File

@ -10,14 +10,14 @@ 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.findById(ctx.request().getParam("id")); User target = User.findByIdSync(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.respondJson(ctx, Grant.findByUser(target)); APIv3.respondJson(ctx, Grant.findByUserSync(target));
} }
} }

View File

@ -16,7 +16,7 @@ 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.findById(ctx.request().getParam("id")); User target = User.findByIdSync(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"));
@ -67,7 +67,7 @@ public final class POSTUserGrant implements Handler<RoutingContext> {
} }
// 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.findById(ctx.request().getParam("addedBy")); User addedBy = User.findByIdSync(ctx.request().getParam("addedBy"));
Grant grant = new Grant(target, reason, scopes, rank, expiresAt, addedBy); Grant grant = new Grant(target, reason, scopes, rank, expiresAt, addedBy);
grant.insert(); grant.insert();

View File

@ -10,14 +10,14 @@ 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.findById(ctx.request().getParam("id")); User target = User.findByIdSync(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.respondJson(ctx, IPLogEntry.findByUser(target)); APIv3.respondJson(ctx, IPLogEntry.findByUserSync(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.findById(ctx.request().getParam("id")); NotificationTemplate notificationTemplate = NotificationTemplate.findByIdSync(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"));

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.respondJson(ctx, NotificationTemplate.findById(ctx.request().getParam("id"))); APIv3.respondJson(ctx, NotificationTemplate.findByIdSync(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.respondJson(ctx, NotificationTemplate.findAll()); APIv3.respondJson(ctx, NotificationTemplate.findAllSync());
} }
} }

View File

@ -13,7 +13,7 @@ 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.findById(ctx.request().getParam("id")); Punishment punishment = Punishment.findByIdSync(ctx.request().getParam("id"));
if (punishment == null) { if (punishment == null) {
ErrorUtils.respondNotFound(ctx, "Punishment", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Punishment", ctx.request().getParam("id"));
@ -23,7 +23,7 @@ public final class DELETEPunishment implements Handler<RoutingContext> {
return; return;
} }
User removedBy = User.findById(ctx.request().getParam("removedBy")); User removedBy = User.findByIdSync(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"));

View File

@ -8,7 +8,7 @@ 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) {
APIv3.respondJson(ctx, Punishment.findById(ctx.request().getParam("id"))); APIv3.respondJson(ctx, Punishment.findByIdSync(ctx.request().getParam("id")));
} }
} }

View File

@ -10,12 +10,12 @@ public final class GETPunishments implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
try { try {
int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit")); int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip"));
int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset")); int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
APIv3.respondJson(ctx, APIv3.getDatastore().createQuery(Punishment.class).order("addedAt").limit(limit).offset(offset).asList()); APIv3.respondJson(ctx, Punishment.findAllPaginatedSync(skip, pageSize));
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
ErrorUtils.respondInvalidInput(ctx, "limit and offset must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
} }
} }

View File

@ -10,14 +10,14 @@ 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.findById(ctx.request().getParam("id")); User target = User.findByIdSync(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.respondJson(ctx, Punishment.findByUser(target)); APIv3.respondJson(ctx, Punishment.findByUserSync(target));
} }
} }

View File

@ -17,7 +17,7 @@ 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.findById(ctx.request().getParam("id")); User target = User.findByIdSync(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"));
@ -34,9 +34,9 @@ public final class POSTUserPunish implements Handler<RoutingContext> {
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 : Punishment.findByUserAndType(target, ImmutableSet.of(type))) { for (Punishment punishment : Punishment.findByUserAndTypeSync(target, ImmutableSet.of(type))) {
if (punishment.isActive()) { if (punishment.isActive()) {
ErrorUtils.respondGeneric(ctx, "A punishment by " + User.findById(punishment.getAddedBy()).getLastUsername() + " already covers this user."); ErrorUtils.respondGeneric(ctx, "A punishment by " + User.findByIdSync(punishment.getAddedBy()).getLastUsername() + " already covers this user.");
return; return;
} }
} }
@ -63,7 +63,7 @@ public final class POSTUserPunish implements Handler<RoutingContext> {
} }
// 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.findById(ctx.request().getParam("addedBy")); User addedBy = User.findByIdSync(ctx.request().getParam("addedBy"));
if (target.hasPermissionAnywhere(Permissions.PROTECTED_PUNISHMENT)) { if (target.hasPermissionAnywhere(Permissions.PROTECTED_PUNISHMENT)) {
ErrorUtils.respondGeneric(ctx, target.getLastSeenOn() + " is protected from punishments."); ErrorUtils.respondGeneric(ctx, target.getLastSeenOn() + " is protected from punishments.");

View File

@ -10,9 +10,8 @@ public final class POSTServerGroup implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
String id = ctx.request().getParam("id"); String id = ctx.request().getParam("id");
String image = ctx.request().getParam("image"); String image = ctx.request().getParam("image");
boolean isPublic = Boolean.valueOf(ctx.request().getParam("public"));
ServerGroup serverGroup = new ServerGroup(id, image, isPublic); ServerGroup serverGroup = new ServerGroup(id, image);
serverGroup.insert(); serverGroup.insert();
APIv3.respondJson(ctx, serverGroup); APIv3.respondJson(ctx, serverGroup);
} }

View File

@ -1,6 +1,9 @@
package net.frozenorb.apiv3.routes.servers; package net.frozenorb.apiv3.routes.servers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -18,7 +21,6 @@ import java.util.*;
@Slf4j @Slf4j
public final class POSTServerHeartbeat implements Handler<RoutingContext> { public final class POSTServerHeartbeat implements Handler<RoutingContext> {
@SuppressWarnings("unchecked")
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");
@ -27,57 +29,132 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
return; return;
} }
Server actorServer = Server.byId(actor.getName()); Server actorServer = Server.findById(actor.getName());
ServerGroup actorServerGroup = ServerGroup.byId(actorServer.getServerGroup()); ServerGroup actorServerGroup = ServerGroup.findById(actorServer.getServerGroup());
Document reqJson = Document.parse(ctx.getBodyAsString()); Document reqJson = Document.parse(ctx.getBodyAsString());
Map<UUID, String> onlinePlayersNames = new HashMap<>(); Map<UUID, String> playerNames = extractPlayerNames(reqJson);
Map<UUID, User> onlinePlayersUsers;
Map<UUID, List<Grant>> onlinePlayersGrants;
Map<UUID, List<Punishment>> onlinePlayersPunishments;
Map<String, Object> playersResponse = new HashMap<>();
// This code is messy, but we have to do db ops in parallel to avoid CompositeFuture.all(
// spamming Mongo with queries, so we do this. createInfoResponse(actorServer, reqJson.getDouble("tps"), playerNames),
for (Object player : (List<Object>) reqJson.get("players")) { createPlayerResponse(actorServer, playerNames),
Document playerJson = (Document) player; createPermissionsResponse(actorServerGroup),
UUID uuid = UUID.fromString(playerJson.getString("uuid")); createEventsResponse((List<Object>) reqJson.get("events"))
String username = playerJson.getString("username"); ).setHandler((result) -> {
if (result.succeeded()) {
if (UUIDUtils.isAcceptableUUID(uuid)) { APIv3.respondJson(ctx, ImmutableMap.of(
onlinePlayersNames.put(uuid, username); "info", result.result().result(0),
"players", result.result().result(1),
"permissions", result.result().result(2),
"events", result.result().result(3)
));
} else {
ErrorUtils.respondGeneric(ctx, result.cause().getMessage());
} }
});
} }
onlinePlayersUsers = User.byIdGrouped(onlinePlayersNames.keySet()); public Future<Map<String, Object>> createInfoResponse(Server server, double tps, Map<UUID, String> playerNames) {
Future<Map<String, Object>> callback = Future.future();
for (Map.Entry<UUID, User> entry : new HashMap<>(onlinePlayersUsers).entrySet()) { server.setPlayers(playerNames.keySet());
UUID uuid = entry.getKey(); server.setLastTps(tps);
User user = entry.getValue(); server.setLastUpdatedAt(new Date());
server.save();
return callback;
}
public Future<Map<String, Object>> createPlayerResponse(Server server, Map<UUID, String> playerNames) {
Future<Map<String, Object>> callback = Future.future();
Future<Map<UUID, User>> userLookupCallback = Future.future();
Future<Map<UUID, List<Grant>>> grantLookupCallback = Future.future();
Future<Map<UUID, List<Punishment>>> punishmentLookupCallback = Future.future();
User.findByIdGrouped(playerNames.keySet(), (users, error) -> {
if (error != null) {
userLookupCallback.fail(error);
} else {
userLookupCallback.complete(users);
}
});
Grant.findByUserGrouped(playerNames.keySet(), (grants, error) -> {
if (error != null) {
grantLookupCallback.fail(error);
} else {
grantLookupCallback.complete(grants);
}
});
Punishment.findByUserGrouped(playerNames.keySet(), (punishments, error) -> {
if (error != null) {
punishmentLookupCallback.fail(error);
} else {
punishmentLookupCallback.complete(punishments);
}
});
CompositeFuture.all(
userLookupCallback,
grantLookupCallback,
punishmentLookupCallback
).setHandler((result) -> {
if (result.failed()) {
callback.fail(result.cause());
} else {
Map<UUID, User> users = result.result().result(0);
Map<UUID, List<Grant>> grants = result.result().result(1);
Map<UUID, List<Punishment>> punishments = result.result().result(2);
Map<String, Object> response = new HashMap<>();
for (Map.Entry<UUID, User> userEntry : users.entrySet()) {
UUID uuid = userEntry.getKey();
User user = userEntry.getValue();
if (user == null) { if (user == null) {
// Will be saved in the User constructor String username = playerNames.get(uuid);
String username = onlinePlayersNames.get(uuid);
user = new User(uuid, username); user = new User(uuid, username);
onlinePlayersUsers.put(uuid, user); user.insert();
users.put(uuid, user);
} }
// Only save if needed // Only save if needed
if (user.seenOnServer(actorServer)) { if (user.seenOnServer(server)) {
APIv3.getDatastore().save(user); user.save();
}
} }
onlinePlayersGrants = Grant.byUserGrouped(onlinePlayersUsers.keySet()); response.put(uuid.toString(), user.createLoginInfo(server, punishments.get(uuid), grants.get(uuid)));
onlinePlayersPunishments = Punishment.byUserGrouped(onlinePlayersUsers.keySet());
for (Map.Entry<UUID, User> entry : onlinePlayersUsers.entrySet()) {
UUID uuid = entry.getKey();
User user = entry.getValue();
playersResponse.put(uuid.toString(), user.prepareLoginInfo(actorServer, onlinePlayersPunishments.get(uuid), onlinePlayersGrants.get(uuid)));
} }
for (Object event : (List<Object>) reqJson.get("events")) { callback.complete(response);
}
});
return callback;
}
public Future<Map<String, Object>> createPermissionsResponse(ServerGroup serverGroup) {
Future<Map<String, Object>> callback = Future.future();
Map<String, Object> permissionsResponse = new HashMap<>();
for (Rank rank : Rank.findAll()) {
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
PermissionUtils.getDefaultPermissions(rank),
serverGroup.calculatePermissions(rank)
);
permissionsResponse.put(rank.getId(), scopedPermissions);
}
callback.complete(permissionsResponse);
return callback;
}
public Future<Map<String, Object>> createEventsResponse(List<Object> eventsData) {
Future<Map<String, Object>> callback = Future.future();
for (Object event : eventsData) {
Document eventJson = (Document) event; Document eventJson = (Document) event;
String type = eventJson.getString("type"); String type = eventJson.getString("type");
@ -93,26 +170,24 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
} }
} }
Map<String, Map<String, Boolean>> permissionsResponse = new HashMap<>(); callback.complete(ImmutableMap.of());
return callback;
for (Rank rank : Rank.findAll()) {
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
PermissionUtils.getDefaultPermissions(rank),
actorServerGroup.calculatePermissions(rank)
);
permissionsResponse.put(rank.getId(), scopedPermissions);
} }
actorServer.setPlayers(onlinePlayersNames.keySet()); public Map<UUID, String> extractPlayerNames(Document reqJson) {
actorServer.setLastTps(reqJson.getDouble("lastTps")); Map<UUID, String> result = new HashMap<>();
actorServer.setLastUpdatedAt(new Date());
actorServer.save();
APIv3.respondJson(ctx, ImmutableMap.of( for (Object player : (List<Object>) reqJson.get("players")) {
"players", playersResponse, Document playerJson = (Document) player;
"permissions", permissionsResponse UUID uuid = UUID.fromString(playerJson.getString("uuid"));
)); String username = playerJson.getString("username");
if (UUIDUtils.isAcceptableUUID(uuid)) {
result.put(uuid, username);
}
}
return result;
} }
} }

View File

@ -12,7 +12,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -26,7 +26,7 @@ public final class DELETEUserMeta implements Handler<RoutingContext> {
return; return;
} }
UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroup(user, serverGroup); UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup);
if (userMetaEntry != null) { if (userMetaEntry != null) {
userMetaEntry.delete(); userMetaEntry.delete();

View File

@ -14,7 +14,7 @@ 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.findById(ctx.request().getParam("id")); User target = User.findByIdSync(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"));
@ -22,7 +22,7 @@ public final class DELETEUserPunishment implements Handler<RoutingContext> {
} }
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.findById(ctx.request().getParam("removedBy")); User removedBy = User.findByIdSync(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"));
@ -36,7 +36,7 @@ public final class DELETEUserPunishment implements Handler<RoutingContext> {
return; return;
} }
for (Punishment punishment : target.getPunishments(ImmutableSet.of(type))) { for (Punishment punishment : Punishment.findByUserAndTypeSync(target, 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());

View File

@ -27,9 +27,9 @@ public final class GETStaff implements Handler<RoutingContext> {
return Integer.compare(firstRank.getWeight(), secondRank.getWeight()); return Integer.compare(firstRank.getWeight(), secondRank.getWeight());
}); });
Grant.findByRank(staffRanks.values()).forEach(grant -> { Grant.findByRankSync(staffRanks.values()).forEach(grant -> {
if (grant.isActive()) { if (grant.isActive()) {
User user = User.findById(grant.getUser()); User user = User.findByIdSync(grant.getUser());
Rank rank = staffRanks.get(grant.getRank()); Rank rank = staffRanks.get(grant.getRank());
if (!result.containsKey(rank.getId())) { if (!result.containsKey(rank.getId())) {

View File

@ -8,7 +8,7 @@ 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) {
APIv3.respondJson(ctx, User.findById(ctx.request().getParam("id"))); APIv3.respondJson(ctx, User.findByIdSync(ctx.request().getParam("id")));
} }
} }

View File

@ -13,7 +13,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -23,9 +23,9 @@ public final class GETUserDetails implements Handler<RoutingContext> {
// Too many fields to use .of() // Too many fields to use .of()
APIv3.respondJson(ctx, ImmutableMap.builder() APIv3.respondJson(ctx, ImmutableMap.builder()
.put("user", user) .put("user", user)
.put("grants", Grant.findByUser(user)) .put("grants", Grant.findByUserSync(user))
.put("ipLog", IPLogEntry.findByUser(user)) .put("ipLog", IPLogEntry.findByUserSync(user))
.put("punishments", Punishment.findByUser(user)) .put("punishments", Punishment.findByUserSync(user))
.put("aliases", user.getAliases()) .put("aliases", user.getAliases())
.put("totpSetup", user.getTotpSecret() != null) .put("totpSetup", user.getTotpSecret() != null)
.build() .build()

View File

@ -11,7 +11,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -25,7 +25,7 @@ public final class GETUserMeta implements Handler<RoutingContext> {
return; return;
} }
UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroup(user, serverGroup); UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup);
APIv3.respondJson(ctx, userMetaEntry.getData()); APIv3.respondJson(ctx, userMetaEntry.getData());
} }

View File

@ -2,9 +2,11 @@ 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.core.Handler;
import io.vertx.core.cli.converters.BooleanConverter;
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.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.IPUtils;
import net.frozenorb.apiv3.utils.TOTPUtils; import net.frozenorb.apiv3.utils.TOTPUtils;
@ -12,7 +14,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -34,7 +36,10 @@ public final class GETUserRequiresTOTP implements Handler<RoutingContext> {
return; return;
} }
if (TOTPUtils.isPreAuthorized(user, userIp)) { BlockingCallback<Boolean> preAuthorizedCallback = new BlockingCallback<>();
TOTPUtils.isPreAuthorized(user, userIp, preAuthorizedCallback);
if (preAuthorizedCallback.get()) {
APIv3.respondJson(ctx, 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"

View File

@ -10,7 +10,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));

View File

@ -22,7 +22,7 @@ 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.findByEmailToken(ctx.request().getParam("emailToken")); User user = User.findByEmailTokenSync(ctx.request().getParam("emailToken"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "Email token", ctx.request().getParam("emailToken")); ErrorUtils.respondNotFound(ctx, "Email token", ctx.request().getParam("emailToken"));

View File

@ -10,7 +10,7 @@ public class POSTUserLeave implements Handler<RoutingContext> {
@Override @Override
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));

View File

@ -5,6 +5,7 @@ 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.actors.ActorType; import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.models.IPLogEntry;
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;
@ -23,7 +24,7 @@ public final class POSTUserLogin implements Handler<RoutingContext> {
return; return;
} }
User user = User.findById(uuid); User user = User.findByIdSync(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");
@ -45,7 +46,19 @@ public final class POSTUserLogin implements Handler<RoutingContext> {
Server actorServer = Server.findById(actor.getName()); Server actorServer = Server.findById(actor.getName());
user.getIPLogEntry(userIp).used(); IPLogEntry ipLogEntry = IPLogEntry.findByUserAndIpSync(user, userIp);
// We use a little bit more verbose code here to save on the
// overhead of a .insert() immediately followed by a .save()
if (ipLogEntry == null) {
ipLogEntry = new IPLogEntry(user, userIp);
ipLogEntry.used();
ipLogEntry.insert();
} else {
ipLogEntry.used();
ipLogEntry.save();
}
user.updateUsername(username); user.updateUsername(username);
APIv3.respondJson(ctx, user.getLoginInfo(actorServer)); APIv3.respondJson(ctx, user.getLoginInfo(actorServer));
} }

View File

@ -6,6 +6,7 @@ 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;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.unsorted.Notification;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
@ -15,7 +16,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -27,7 +28,7 @@ public final class POSTUserNotify implements Handler<RoutingContext> {
return; return;
} }
NotificationTemplate template = NotificationTemplate.findById(ctx.request().getParam("template")); NotificationTemplate template = NotificationTemplate.findByIdSync(ctx.request().getParam("template"));
if (template == null) { if (template == null) {
ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("template")); ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("template"));
@ -49,7 +50,10 @@ public final class POSTUserNotify implements Handler<RoutingContext> {
try { try {
Notification notification = new Notification(template, subjectReplacements, bodyReplacements); Notification notification = new Notification(template, subjectReplacements, bodyReplacements);
notification.sendAsEmail(user.getEmail()); BlockingCallback<Void> callback = new BlockingCallback<>();
notification.sendAsEmail(user.getEmail(), callback);
callback.get();
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"success", true "success", true
)); ));

View File

@ -6,6 +6,7 @@ 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;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.unsorted.Notification;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
@ -24,7 +25,7 @@ public final class POSTUserRegister implements Handler<RoutingContext> {
); );
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -59,10 +60,12 @@ public final class POSTUserRegister implements Handler<RoutingContext> {
"emailToken", user.getEmailToken() "emailToken", user.getEmailToken()
); );
Notification notification = new Notification(NotificationTemplate.findById("email-confirmation"), replacements, replacements); Notification notification = new Notification(NotificationTemplate.findByIdSync("email-confirmation"), replacements, replacements);
try { try {
notification.sendAsEmail(user.getEmail()); BlockingCallback<Void> callback = new BlockingCallback<>();
notification.sendAsEmail(user.getEmail(), callback);
callback.get();
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"success", true "success", true
)); ));

View File

@ -12,7 +12,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));

View File

@ -2,9 +2,12 @@ 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.core.Handler;
import io.vertx.core.cli.converters.BooleanConverter;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
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.unsorted.BlockingCallback;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.IPUtils;
import net.frozenorb.apiv3.utils.TOTPUtils; import net.frozenorb.apiv3.utils.TOTPUtils;
@ -14,7 +17,7 @@ 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -34,8 +37,10 @@ public final class POSTUserVerifyTOTP implements Handler<RoutingContext> {
} }
int providedCode = Integer.parseInt(ctx.request().getParam("code")); int providedCode = Integer.parseInt(ctx.request().getParam("code"));
BlockingCallback<Boolean> recentlyUsedCallback = new BlockingCallback<>();
TOTPUtils.wasRecentlyUsed(user, providedCode, recentlyUsedCallback);
if (TOTPUtils.wasRecentlyUsed(user, providedCode)) { if (recentlyUsedCallback.get()) {
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", false, "authorized", false,
"message", "TOTP code was recently used." "message", "TOTP code was recently used."
@ -46,8 +51,13 @@ public final class POSTUserVerifyTOTP implements Handler<RoutingContext> {
boolean authorized = TOTPUtils.authorizeUser(user, providedCode); boolean authorized = TOTPUtils.authorizeUser(user, providedCode);
if (authorized) { if (authorized) {
TOTPUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS); BlockingCallback<Void> markPreAuthorizedCallback = new BlockingCallback<>();
TOTPUtils.markRecentlyUsed(user, providedCode); TOTPUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS, markPreAuthorizedCallback);
markPreAuthorizedCallback.get();
BlockingCallback<Void> markRecentlyUsedCallback = new BlockingCallback<>();
TOTPUtils.markRecentlyUsed(user, providedCode, markRecentlyUsedCallback);
markRecentlyUsedCallback.get();
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", true, "authorized", true,

View File

@ -5,13 +5,14 @@ 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;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.models.UserMetaEntry;
import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.ErrorUtils;
import org.bson.Document; 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.findById(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
@ -26,8 +27,16 @@ public final class PUTUserMeta implements Handler<RoutingContext> {
} }
Document data = Document.parse(ctx.getBodyAsString()); Document data = Document.parse(ctx.getBodyAsString());
UserMetaEntry metaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup);
if (metaEntry == null) {
metaEntry = new UserMetaEntry(user, serverGroup, data);
metaEntry.insert();
} else {
metaEntry.setData(data);
metaEntry.save();
}
user.saveMeta(serverGroup, data);
APIv3.respondJson(ctx, data); APIv3.respondJson(ctx, data);
} }

View File

@ -0,0 +1,40 @@
package net.frozenorb.apiv3.serialization;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistry;
import java.util.UUID;
public final class MineHQCodecProvider implements CodecProvider {
public <T> Codec<T> get(Class<T> clazz, CodecRegistry codecRegistry) {
if (clazz == UUID.class) {
return (Codec<T>) new Codec<UUID>() {
@Override
public UUID decode(BsonReader bsonReader, DecoderContext decoderContext) {
return UUID.fromString(bsonReader.readString());
}
@Override
public void encode(BsonWriter bsonWriter, UUID uuid, EncoderContext encoderContext) {
bsonWriter.writeString(uuid == null ? null : uuid.toString());
}
@Override
public Class<UUID> getEncoderClass() {
return UUID.class;
}
};
} else {
return null;
}
}
}

View File

@ -1,23 +0,0 @@
package net.frozenorb.apiv3.serialization;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.bson.types.ObjectId;
import java.io.IOException;
public final class ObjectIdTypeAdapter extends TypeAdapter<ObjectId> {
public void write(JsonWriter writer, ObjectId write) throws IOException {
writer.value(write.toString());
}
// This is used with Gson, which is only used
// to serialize outgoing responses, thus we
// don't need to have a read method.
public ObjectId read(JsonReader reader) {
throw new IllegalArgumentException();
}
}

View File

@ -0,0 +1,18 @@
package net.frozenorb.apiv3.serialization;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.util.UUID;
public class UUIDJsonDeserializer extends JsonDeserializer<UUID> {
@Override
public UUID deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return UUID.fromString(jsonParser.getValueAsString());
}
}

View File

@ -0,0 +1,18 @@
package net.frozenorb.apiv3.serialization;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.UUID;
public final class UUIDJsonSerializer extends JsonSerializer<UUID> {
@Override
public void serialize(UUID uuid, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(uuid.toString());
}
}

View File

@ -9,6 +9,7 @@ public final class BlockingCallback<T> implements SingleResultCallback<T> {
private final SettableFuture<T> future = SettableFuture.create(); private final SettableFuture<T> future = SettableFuture.create();
@Override
public void onResult(T val, Throwable error) { public void onResult(T val, Throwable error) {
if (error != null) { if (error != null) {
future.setException(error); future.setException(error);

View File

@ -5,6 +5,7 @@ import com.cribbstechnologies.clients.mandrill.model.MandrillHtmlMessage;
import com.cribbstechnologies.clients.mandrill.model.MandrillMessageRequest; import com.cribbstechnologies.clients.mandrill.model.MandrillMessageRequest;
import com.cribbstechnologies.clients.mandrill.model.MandrillRecipient; import com.cribbstechnologies.clients.mandrill.model.MandrillRecipient;
import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest; import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest;
import com.mongodb.async.SingleResultCallback;
import com.twilio.sdk.TwilioRestException; import com.twilio.sdk.TwilioRestException;
import com.twilio.sdk.resource.factory.MessageFactory; import com.twilio.sdk.resource.factory.MessageFactory;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
@ -32,7 +33,7 @@ public final class Notification {
this.body = template.fillBody(bodyReplacements); this.body = template.fillBody(bodyReplacements);
} }
public void sendAsEmail(String email) throws IOException { public void sendAsEmail(String email, SingleResultCallback<Void> callback) throws IOException {
MandrillHtmlMessage message = new MandrillHtmlMessage(); MandrillHtmlMessage message = new MandrillHtmlMessage();
message.setFrom_email("no-reply@minehq.com"); message.setFrom_email("no-reply@minehq.com");
@ -43,31 +44,49 @@ public final class Notification {
new MandrillRecipient(null, email) new MandrillRecipient(null, email)
}); });
APIv3.getVertxInstance().executeBlocking((future) -> {
try { try {
MandrillMessageRequest request = new MandrillMessageRequest(); MandrillMessageRequest request = new MandrillMessageRequest();
request.setMessage(message); request.setMessage(message);
mandrillMessagesRequest.sendMessage(request); mandrillMessagesRequest.sendMessage(request);
APIv3.getStatsD().incrementCounter("apiv3.notification.email.success"); APIv3.getStatsD().incrementCounter("apiv3.notification.email.success");
future.succeeded();
} catch (RequestFailedException ex) { } catch (RequestFailedException ex) {
APIv3.getStatsD().incrementCounter("apiv3.notification.email.failure"); APIv3.getStatsD().incrementCounter("apiv3.notification.email.failure");
throw new IOException("Failed to send notification to user", ex); future.fail(new IOException("Failed to send notification to user", ex));
} }
}, (result) -> {
if (result.succeeded()) {
callback.onResult(null, null);
} else {
callback.onResult(null, result.cause());
}
});
} }
public void sendAsText(String phoneNumber) throws IOException { public void sendAsText(String phoneNumber, SingleResultCallback<Void> callback) throws IOException {
List<NameValuePair> params = new ArrayList<>(); List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("To", phoneNumber)); params.add(new BasicNameValuePair("To", phoneNumber));
params.add(new BasicNameValuePair("From", "+13108795180")); params.add(new BasicNameValuePair("From", "+13108795180"));
params.add(new BasicNameValuePair("Body", body)); params.add(new BasicNameValuePair("Body", body));
APIv3.getVertxInstance().executeBlocking((future) -> {
try { try {
twillioMessageFactory.create(params); twillioMessageFactory.create(params);
APIv3.getStatsD().incrementCounter("apiv3.notification.text.success"); APIv3.getStatsD().incrementCounter("apiv3.notification.text.success");
future.succeeded();
} catch (TwilioRestException ex) { } catch (TwilioRestException ex) {
APIv3.getStatsD().incrementCounter("apiv3.notification.text.failure"); APIv3.getStatsD().incrementCounter("apiv3.notification.text.failure");
throw new IOException("Failed to send notification to user", ex); future.fail(new IOException("Failed to send notification to user", ex));
} }
}, (result) -> {
if (result.succeeded()) {
callback.onResult(null, null);
} else {
callback.onResult(null, result.cause());
}
});
} }
} }

View File

@ -6,5 +6,6 @@ import lombok.experimental.UtilityClass;
public class Permissions { public class Permissions {
public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected"; public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected";
public static final String SIGN_API_REQUEST = "apiv3.signRequest";
} }

View File

@ -11,12 +11,6 @@ import java.util.UUID;
@UtilityClass @UtilityClass
public class MojangUtils { public class MojangUtils {
public static String getName(UUID id) {
BlockingCallback<String> callback = new BlockingCallback<>();
getName(id, callback);
return callback.get();
}
public static void getName(UUID id, SingleResultCallback<String> callback) { public static void getName(UUID id, SingleResultCallback<String> callback) {
APIv3.getHttpClient().get("sessionserver.mojang.com", "session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> { APIv3.getHttpClient().get("sessionserver.mojang.com", "session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> {
response.bodyHandler((body) -> { response.bodyHandler((body) -> {

View File

@ -1,15 +1,18 @@
package net.frozenorb.apiv3.utils; package net.frozenorb.apiv3.utils;
import com.mongodb.async.SingleResultCallback;
import com.warrenstrange.googleauth.GoogleAuthenticator; import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorConfig; import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;
import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.models.User;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
@UtilityClass @UtilityClass
public class TOTPUtils { public class TOTPUtils {
@ -38,34 +41,60 @@ public class TOTPUtils {
); );
} }
public static boolean isPreAuthorized(User user, String ip) { public static void isPreAuthorized(User user, String ip, SingleResultCallback<Boolean> callback) {
try (Jedis redis = APIv3.getRedisPool().getResource()) { APIv3.getRedisClient().exists(user.getId() + ":preAuthorizedIP:" + ip.toLowerCase(), (result) -> {
return redis.exists(user.getId() + ":preAuthorizedIP:" + ip.toLowerCase()); if (result.succeeded()) {
callback.onResult(result.result() == 1 , null);
} else {
callback.onResult(null, result.cause());
} }
});
} }
public static void markPreAuthorized(User user, String ip, long duration, TimeUnit unit) { public static void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback<Void> callback) {
try (Jedis redis = APIv3.getRedisPool().getResource()) {
String key = user.getId() + ":preAuthorizedIP:" + ip.toLowerCase(); String key = user.getId() + ":preAuthorizedIP:" + ip.toLowerCase();
redis.set(key, ""); APIv3.getRedisClient().set(key, "", (result) -> {
redis.expire(key, (int) unit.toSeconds(duration)); if (result.succeeded()) {
APIv3.getRedisClient().expire(key, (int) unit.toSeconds(duration), (result2) -> {
if (result2.succeeded()) {
callback.onResult(null, null);
} else {
callback.onResult(null, result.cause());
} }
});
} else {
callback.onResult(null, result.cause());
}
});
} }
public static boolean wasRecentlyUsed(User user, int code) { public static void wasRecentlyUsed(User user, int code, SingleResultCallback<Boolean> callback) {
try (Jedis redis = APIv3.getRedisPool().getResource()) { APIv3.getRedisClient().exists(user.getId() + ":recentTOTPCodes:" + code, (result) -> {
return redis.exists(user.getId() + ":recentTOTPCodes:" + code); if (result.succeeded()) {
callback.onResult(result.result() == 1 , null);
} else {
callback.onResult(null, result.cause());
} }
});
} }
public static void markRecentlyUsed(User user, int code) { public static void markRecentlyUsed(User user, int code, SingleResultCallback<Void> callback) {
try (Jedis redis = APIv3.getRedisPool().getResource()) {
String key = user.getId() + ":recentTOTPCodes:" + code; String key = user.getId() + ":recentTOTPCodes:" + code;
redis.set(key, ""); APIv3.getRedisClient().set(key, "", (result) -> {
redis.expire(key, (int) TimeUnit.MINUTES.toSeconds(5)); if (result.succeeded()) {
APIv3.getRedisClient().expire(key, (int) TimeUnit.MINUTES.toSeconds(5), (result2) -> {
if (result2.succeeded()) {
callback.onResult(null, null);
} else {
callback.onResult(null, result.cause());
} }
});
} else {
callback.onResult(null, result.cause());
}
});
} }
} }