diff --git a/apiv3.properties b/apiv3.properties index e307d26..74192b2 100644 --- a/apiv3.properties +++ b/apiv3.properties @@ -1,6 +1,4 @@ general.releaseStage=production -logging.level=info -logging.debug=true mongo.address=localhost mongo.port=27017 mongo.database=MineHQ @@ -13,6 +11,5 @@ twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740 twillio.authToken=982592505a171d3be6b0722f5ecacc0e mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ bugsnag.apiKey=0e47fba8b825416b7cbc839066184509 -auth.permittedUserRanks=developer,owner auth.websiteApiKey=RVbp4hY6sCFVaf auth.bungeeCordApiKey=6d9cf76dc9f0d23 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2ce866f..729b473 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,10 @@ minehq-repo http://maven.minehq.com:8081/artifactory/minehq-all/ + + mongo-jackson-codec-repo + https://dl.bintray.com/ylemoigne/maven + @@ -62,12 +66,17 @@ io.vertx vertx-core - LATEST + 3.2.1 io.vertx vertx-web - LATEST + 3.2.1 + + + io.vertx + vertx-redis-client + 3.2.1 @@ -89,16 +98,14 @@ 3.0.4 - eu.dozd - mongo-mapper - 1.0.1 + fr.javatic.mongo + mongo-jackson-codec + 3.2.0__0.4 - - - redis.clients - jedis - 2.8.1 + de.undercouch + bson4jackson + 2.6.0 diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index a82c07c..08a02e2 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -1,39 +1,50 @@ package net.frozenorb.apiv3; 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.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.mongodb.Block; +import com.mongodb.ConnectionString; import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; import com.mongodb.async.client.MongoClient; import com.mongodb.async.client.MongoClientSettings; import com.mongodb.async.client.MongoClients; import com.mongodb.async.client.MongoDatabase; +import com.mongodb.connection.ClusterConnectionMode; import com.mongodb.connection.ClusterSettings; import com.timgroup.statsd.NonBlockingStatsDClient; 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.Handler; +import io.vertx.core.Vertx; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpServer; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.BodyHandler; 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.extern.slf4j.Slf4j; import net.frozenorb.apiv3.actors.ActorType; -import net.frozenorb.apiv3.filters.ActorAttributeFilter; -import net.frozenorb.apiv3.filters.AuthorizationFilter; -import net.frozenorb.apiv3.filters.MetricsHandler; -import net.frozenorb.apiv3.models.Grant; -import net.frozenorb.apiv3.models.IPLogEntry; -import net.frozenorb.apiv3.models.Punishment; -import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.handlers.ActorAttributeHandler; +import net.frozenorb.apiv3.handlers.AuthorizationHandler; +import net.frozenorb.apiv3.handlers.MetricsHandler; +import net.frozenorb.apiv3.models.*; import net.frozenorb.apiv3.routes.GETDump; import net.frozenorb.apiv3.routes.GETWhoAmI; 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.servers.*; import net.frozenorb.apiv3.routes.users.*; -import net.frozenorb.apiv3.serialization.DateTypeAdapter; -import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy; -import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter; +import net.frozenorb.apiv3.serialization.*; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger; import net.frozenorb.apiv3.utils.IPUtils; +import net.frozenorb.apiv3.utils.SyncUtils; import net.frozenorb.apiv3.utils.UUIDUtils; 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.types.ObjectId; -import redis.clients.jedis.JedisPool; import java.io.FileInputStream; import java.io.InputStream; +import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -80,26 +95,27 @@ public final class APIv3 extends AbstractVerticle { @Getter private static HttpClient httpClient; @Getter private static MongoDatabase database; @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 Vertx vertxInstance; @Getter private static final Gson gson = new GsonBuilder() - .registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter()) .registerTypeAdapter(Date.class, new DateTypeAdapter()) .setExclusionStrategies(new FollowAnnotationExclusionStrategy()) .create(); @Override public void start() { - setupConfig(); - System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", config.getProperty("logging.level")); + vertxInstance = vertx; + setupConfig(); setupDatabase(); setupRedis(); setupMetrics(); setupBugsnag(); - setupHttpServer(); + //setupHttpServer(); + setupHttpClient(); - //convertData("158.69.126.126", true); + convertData("mongodb://158.69.126.126", true); } private void setupConfig() { @@ -125,18 +141,37 @@ public final class APIv3 extends AbstractVerticle { ClusterSettings clusterSettings = ClusterSettings .builder() - .hosts(ImmutableList.of( - new ServerAddress( - config.getProperty("mongo.address"), - Integer.parseInt(config.getProperty("mongo.port")) - ) - )) + .applyConnectionString(new ConnectionString("mongodb://" + config.getProperty("mongo.address") + ":" + config.getProperty("mongo.port"))) .build(); + + List 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 .builder() - .codecRegistry(CodecRegistries.fromProviders(MongoMapper.getProviders())) + .codecRegistry(CodecRegistries.fromProviders(providers)) .credentialList(credentials) - .clusterSettings(clusterSettings).build(); + .clusterSettings(clusterSettings) + .build(); MongoClient client = MongoClients.create(settings); database = client.getDatabase(config.getProperty("mongo.database")); @@ -144,9 +179,11 @@ public final class APIv3 extends AbstractVerticle { } private void setupRedis() { - redisPool = new JedisPool( - config.getProperty("redis.address"), - Integer.parseInt(config.getProperty("redis.port")) + redisClient = RedisClient.create( + vertx, + 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); mainRouter.route().handler(new MetricsHandler()); - mainRouter.route().handler(new ActorAttributeFilter()); - mainRouter.route().handler(new AuthorizationFilter()); + mainRouter.route().handler(new ActorAttributeHandler()); + mainRouter.route().handler(new AuthorizationHandler()); mainRouter.route().handler(LoggerHandler.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("/servers").blockingHandler(new GETServers()); - mainRouter.post("/server/heartbeat").blockingHandler(new POSTServerHeartbeat()); + mainRouter.post("/server/heartbeat").handler(new POSTServerHeartbeat()); mainRouter.post("/server").blockingHandler(new POSTServer()); //put("/server/:id").blockingHandler(new PUTServer()); 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.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 handler = (Handler) field.get(impl); + + if (handler instanceof BlockingHandlerDecorator) { + field = BlockingHandlerDecorator.class.getDeclaredField("decoratedHandler"); + field.setAccessible(true); + handler = (Handler) field.get(handler); + } + + System.out.println(route.getPath() + " is handled by " + handler.getClass()); + } catch (Exception ex) { + + } }); int port = Integer.parseInt(config.getProperty("http.port")); @@ -276,174 +328,156 @@ public final class APIv3 extends AbstractVerticle { AtomicInteger skippedGrants = new AtomicInteger(); AtomicInteger skippedIpLogs = new AtomicInteger(); - importFrom.getCollection("user").find().forEach(new Block() { + SyncUtils.blockMulti(importFrom.getCollection("user").find()).forEach((user) -> { + String uuidString = String.valueOf(user.get("uuid")); - @Override - public void apply(Document user) { - String uuidString = String.valueOf(user.get("uuid")); - - if (uuidString == null || uuidString.length() != 32) { - return; - } - - UUID uuid = UUID.fromString(uuidString.replaceFirst( "([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5" )); - - if (!UUIDUtils.isAcceptableUUID(uuid)) { - skippedUsers.incrementAndGet(); - return; - } - - mongoIdToUUID.put(user.getObjectId("_id"), uuid); - - User created = new User( - uuid, - String.valueOf(user.get("name")).toString(), - ImmutableMap.of(), - null, - null, - null, - null, - user.getString("email"), - user.getString("phone"), - "INVALID", - user.getDate("joined"), - user.getDate("joined"), - false - ); - - if (forReal) { - created.insert(); - } - - log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")"); + if (uuidString == null || uuidString.length() != 32) { + return; } - }, (a, b) -> {}); - importFrom.getCollection("punishment").find().forEach(new Block() { + UUID uuid = UUID.fromString(uuidString.replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5")); - @Override - public void apply(Document punishment) { - UUID target = mongoIdToUUID.get(((DBRef) punishment.get("user")).getId()); - - if (target == null) { - skippedPunishments.incrementAndGet(); - return; - } - - // Old punishments have this value set to false to indicate they're not active anymore. - if (punishment.containsKey("active") && !punishment.getBoolean("active")) { - return; - } - - com.mongodb. - - Punishment created = new Punishment( - new ObjectId().toString(), - target, - punishment.getString("reason").toString(), - Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()), - punishment.getDate("expires"), - 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, - (Date) punishment.getDate("created").clone(), - punishment.containsKey("createdOn") ? String.valueOf(((DBRef) punishment.get("createdOn")).getId()) : "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.getDate("created"), - punishment.containsKey("removalReason") ? punishment.getString("removalReason").toString() : "" - ); - - if (forReal) { - created.insert(); - } - - log.info("Created punishment " + created.getId() + " (" + created.getType() + ")"); + if (!UUIDUtils.isAcceptableUUID(uuid)) { + skippedUsers.incrementAndGet(); + return; } - }, (a, b) -> {}); - importFrom.getCollection("grant").find().forEach(new Block() { + mongoIdToUUID.put(user.getObjectId("_id"), uuid); - @Override - public void apply(Document grant) { - UUID target = mongoIdToUUID.get(((DBRef) grant.get("target")).getId()); + User created = new User( + uuid, + String.valueOf(user.get("name")).toString(), + ImmutableMap.of(user.getString("name"), user.getDate("joined")), + null, + null, + null, + null, + user.getString("email"), + user.getString("phone"), + "INVALID", + user.getDate("joined"), + user.getDate("joined"), + false + ); - if (target == null) { - skippedGrants.incrementAndGet(); - return; - } - - String rank = grant.getString("role"); - - if (rank.equalsIgnoreCase("unban") || rank.equalsIgnoreCase("pass") || rank.equalsIgnoreCase("pink") || rank.equalsIgnoreCase("jrdev")) { - return; - } else if (rank.equalsIgnoreCase("high_roller")) { - rank = "high-roller"; - } else if (rank.equalsIgnoreCase("dev")) { - rank = "developer"; - } - - Grant created = new Grant( - new ObjectId().toString(), - target, - grant.containsKey("comment") ? grant.getString("comment") : "", - grant.containsKey("scope") ? ImmutableSet.copyOf((Collection) grant.get("scope")) : ImmutableSet.of(), - rank, - grant.getDate("expires"), - grant.containsKey("addedBy") ? mongoIdToUUID.get(((DBRef) grant.get("addedBy")).getId()) : null, - grant.containsKey("created") ? grant.getDate("created") : new Date(), - null, - null, - null - ); - - if (forReal) { - created.insert(); - } - - log.info("Created grant " + created.getId() + " (" + created.getRank() + ")"); + if (forReal) { + created.insert(); } - }, (a, b) -> {}); - importFrom.getCollection("iplog").find().forEach(new Block() { + log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")"); + }); - @Override - public void apply(Document ipLogEntry) { - UUID user = mongoIdToUUID.get(((DBRef) ipLogEntry.get("user")).getId()); + SyncUtils.blockMulti(importFrom.getCollection("punishment").find()).forEach((punishment) -> { + UUID target = mongoIdToUUID.get(((Map) punishment.get("user")).get("$id")); - if (user == null || ipLogEntry.getString("ip") == null) { - skippedIpLogs.incrementAndGet(); - return; - } - - String ip = ipLogEntry.getString("ip").replace("/", ""); - - if (!IPUtils.isValidIP(ip)) { - return; - } - - Date lastSeen = ipLogEntry.getDate("lastSeen"); - - if (lastSeen == null) { - lastSeen = new Date(); - } - - IPLogEntry created = new IPLogEntry( - new ObjectId().toString(), - user, - ip, - lastSeen, - lastSeen, - ((Number) ipLogEntry.get("uses")).intValue() - - ); - - if (forReal) { - created.insert(); - } - - log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")"); + if (target == null) { + skippedPunishments.incrementAndGet(); + return; } - }, (a, b) -> {}); + + // Old punishments have this value set to false to indicate they're not active anymore. + if (punishment.containsKey("active") && !punishment.getBoolean("active")) { + return; + } + + Punishment created = new Punishment( + new ObjectId().toString(), + target, + punishment.getString("reason").toString(), + Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()), + punishment.getDate("expires"), + punishment.containsKey("meta") ? (punishment.get("meta") instanceof List ? ImmutableMap.of() : (Document) punishment.get("meta")) : ImmutableMap.of(), + punishment.containsKey("addedBy") ? mongoIdToUUID.get(((Map) punishment.get("addedBy")).get("$id")) : null, + (Date) punishment.getDate("created").clone(), + punishment.containsKey("createdOn") ? String.valueOf(((Map) punishment.get("createdOn")).get("$id")) : "Website", + punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE, + punishment.containsKey("removedBy") ? (((Map) punishment.get("removedBy")).get("$ref").equals("user") ? mongoIdToUUID.get(((Map) punishment.get("removedBy")).get("$id")) : null) : null, + punishment.getDate("created"), + punishment.containsKey("removalReason") ? punishment.getString("removalReason").toString() : "" + ); + + if (forReal) { + created.insert(); + } + + log.info("Created punishment " + created.getId() + " (" + created.getType() + ")"); + }); + + SyncUtils.blockMulti(importFrom.getCollection("grant").find()).forEach((grant) -> { + UUID target = mongoIdToUUID.get(((Map) grant.get("target")).get("$id")); + + if (target == null) { + skippedGrants.incrementAndGet(); + return; + } + + String rank = grant.getString("role"); + + if (rank.equalsIgnoreCase("unban") || rank.equalsIgnoreCase("pass") || rank.equalsIgnoreCase("pink") || rank.equalsIgnoreCase("jrdev")) { + return; + } else if (rank.equalsIgnoreCase("high_roller")) { + rank = "high-roller"; + } else if (rank.equalsIgnoreCase("dev")) { + rank = "developer"; + } + + Grant created = new Grant( + new ObjectId().toString(), + target, + grant.containsKey("comment") ? grant.getString("comment") : "", + grant.containsKey("scope") ? ImmutableSet.copyOf((Collection) grant.get("scope")) : ImmutableSet.of(), + rank, + grant.getDate("expires"), + grant.containsKey("addedBy") ? mongoIdToUUID.get(((Map) grant.get("addedBy")).get("$id")) : null, + grant.containsKey("created") ? grant.getDate("created") : new Date(), + null, + null, + null + ); + + if (forReal) { + created.insert(); + } + + log.info("Created grant " + created.getId() + " (" + created.getRank() + ")"); + }); + + SyncUtils.blockMulti(importFrom.getCollection("iplog").find()).forEach((ipLogEntry) -> { + UUID user = mongoIdToUUID.get(((Map) ipLogEntry.get("user")).get("$id")); + + if (user == null || ipLogEntry.getString("ip") == null) { + skippedIpLogs.incrementAndGet(); + return; + } + + String ip = ipLogEntry.getString("ip").replace("/", ""); + + if (!IPUtils.isValidIP(ip)) { + return; + } + + Date lastSeen = ipLogEntry.getDate("lastSeen"); + + if (lastSeen == null) { + lastSeen = new Date(); + } + + IPLogEntry created = new IPLogEntry( + new ObjectId().toString(), + user, + ip, + lastSeen, + lastSeen, + ((Number) ipLogEntry.get("uses")).intValue() + + ); + + if (forReal) { + created.insert(); + } + + log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")"); + }); log.info("Skipped " + skippedUsers.get() + " users, " + skippedPunishments.get() + " punishments, " + skippedGrants.get() + " grants, and " + skippedIpLogs.get() + " ip logs"); } diff --git a/src/main/java/net/frozenorb/apiv3/Main.java b/src/main/java/net/frozenorb/apiv3/Main.java index 1c60617..d7b382a 100644 --- a/src/main/java/net/frozenorb/apiv3/Main.java +++ b/src/main/java/net/frozenorb/apiv3/Main.java @@ -5,6 +5,8 @@ import io.vertx.core.Vertx; final class Main { 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()); } diff --git a/src/main/java/net/frozenorb/apiv3/actors/BungeeCordActor.java b/src/main/java/net/frozenorb/apiv3/actors/BungeeCordActor.java index aa4734d..c1fa682 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/BungeeCordActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/BungeeCordActor.java @@ -2,14 +2,17 @@ package net.frozenorb.apiv3.actors; public final class BungeeCordActor implements Actor { + @Override public boolean isAuthorized() { return true; } + @Override public String getName() { return "BungeeCord"; } + @Override public ActorType getType() { return ActorType.BUNGEECORD; } diff --git a/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java index 43a3411..07347d8 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java @@ -1,5 +1,6 @@ package net.frozenorb.apiv3.actors; +import lombok.AllArgsConstructor; import lombok.Getter; import net.frozenorb.apiv3.models.Server; @@ -11,14 +12,17 @@ public final class ServerActor implements Actor { this.server = server; } + @Override public boolean isAuthorized() { return true; } + @Override public String getName() { return server.getId(); } + @Override public ActorType getType() { return ActorType.SERVER; } diff --git a/src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java b/src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java index 9d89614..e275a80 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/UnknownActor.java @@ -2,14 +2,17 @@ package net.frozenorb.apiv3.actors; public final class UnknownActor implements Actor { + @Override public boolean isAuthorized() { return false; } + @Override public String getName() { return "Unknown"; } + @Override public ActorType getType() { return ActorType.UNKNOWN; } diff --git a/src/main/java/net/frozenorb/apiv3/actors/UserActor.java b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java index 0da25d3..3873270 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/UserActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java @@ -4,35 +4,40 @@ import com.google.common.collect.ImmutableSet; import lombok.Getter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.Permissions; import java.util.Set; public final class UserActor implements Actor { - private static final Set permittedUserRanks = ImmutableSet.copyOf(APIv3.getConfig().getProperty("auth.permittedUserRanks").toLowerCase().split(",")); - @Getter private final User user; // 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; public UserActor(User user) { this.user = user; } + @Override public boolean isAuthorized() { if (cachedAuthorized != null) { return cachedAuthorized; } else { - String highestRankId = user.getHighestRankAnywhere().getId(); - cachedAuthorized = permittedUserRanks.contains(highestRankId.toLowerCase()); - return cachedAuthorized; + boolean authorized = user.hasPermissionAnywhere(Permissions.SIGN_API_REQUEST); + cachedAuthorized = authorized; + return authorized; } } + @Override public String getName() { return user.getLastUsername(); } + @Override public ActorType getType() { return ActorType.USER; } diff --git a/src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java b/src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java index 939cf01..ae0e518 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/WebsiteActor.java @@ -2,14 +2,17 @@ package net.frozenorb.apiv3.actors; public final class WebsiteActor implements Actor { + @Override public boolean isAuthorized() { return true; } + @Override public String getName() { return "Website"; } + @Override public ActorType getType() { return ActorType.WEBSITE; } diff --git a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java b/src/main/java/net/frozenorb/apiv3/handlers/ActorAttributeHandler.java similarity index 57% rename from src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java rename to src/main/java/net/frozenorb/apiv3/handlers/ActorAttributeHandler.java index 3c47fbf..8fc1e86 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java +++ b/src/main/java/net/frozenorb/apiv3/handlers/ActorAttributeHandler.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.filters; +package net.frozenorb.apiv3.handlers; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; @@ -10,41 +10,46 @@ import net.frozenorb.apiv3.utils.ErrorUtils; import java.util.Base64; -public final class ActorAttributeFilter implements Handler { +public final class ActorAttributeHandler implements Handler { - // TODO: MAKE THIS ASYNC + @Override public void handle(RoutingContext ctx) { - String authHeader = ctx.request().getHeader("Authorization"); - String mhqAuthHeader = ctx.request().getHeader("MHQ-Authorization"); + String authorizationHeader = ctx.request().getHeader("Authorization"); + String mhqAuthorizationHeader = ctx.request().getHeader("MHQ-Authorization"); - if (authHeader != null) { - processBasicAuthorization(authHeader, ctx); - } else if (mhqAuthHeader != null) { - processMHQAuthorization(mhqAuthHeader, ctx); + if (authorizationHeader != null) { + processBasicAuthorization(authorizationHeader, ctx); + } else if (mhqAuthorizationHeader != null) { + processMHQAuthorization(mhqAuthorizationHeader, ctx); } else { - ctx.put("actor", new UnknownActor()); - ctx.next(); + processNoAuthorization(ctx); } } - @SuppressWarnings("deprecation") // We purposely get the User by their last username. private void processBasicAuthorization(String authHeader, RoutingContext ctx) { 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) { - User user = User.findByLastUsername(credentials[0]); - String password = credentials[1]; + User.findByLastUsername(credentials[0], (user, error) -> { + if (error != null) { + String password = credentials[1]; - if (user != null && user.getPassword() != null && user.checkPassword(password)) { - ctx.put("actor", new UserActor(user)); - ctx.next(); - return; - } + if (user != null && user.getPassword() != null && user.checkPassword(password)) { + ctx.put("actor", new UserActor(user)); + ctx.next(); + return; + } + } + + ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\""); + 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] + "."); } - - ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\""); - ErrorUtils.respondGeneric(ctx, "Failed to authorize as " + credentials[0] + "."); } private void processMHQAuthorization(String authHeader, RoutingContext ctx) { @@ -93,4 +98,9 @@ public final class ActorAttributeFilter implements Handler { ErrorUtils.respondGeneric(ctx, "Failed to authorize."); } + public void processNoAuthorization(RoutingContext ctx) { + ctx.put("actor", new UnknownActor()); + ctx.next(); + } + } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java b/src/main/java/net/frozenorb/apiv3/handlers/AuthorizationHandler.java similarity index 84% rename from src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java rename to src/main/java/net/frozenorb/apiv3/handlers/AuthorizationHandler.java index b078397..b25bf01 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/AuthorizationFilter.java +++ b/src/main/java/net/frozenorb/apiv3/handlers/AuthorizationHandler.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.filters; +package net.frozenorb.apiv3.handlers; import io.vertx.core.Handler; 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.utils.ErrorUtils; -public final class AuthorizationFilter implements Handler { +public final class AuthorizationHandler implements Handler { + @Override public void handle(RoutingContext ctx) { Actor actor = ctx.get("actor"); diff --git a/src/main/java/net/frozenorb/apiv3/filters/MetricsHandler.java b/src/main/java/net/frozenorb/apiv3/handlers/MetricsHandler.java similarity index 85% rename from src/main/java/net/frozenorb/apiv3/filters/MetricsHandler.java rename to src/main/java/net/frozenorb/apiv3/handlers/MetricsHandler.java index 5d0fa69..1bb0839 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/MetricsHandler.java +++ b/src/main/java/net/frozenorb/apiv3/handlers/MetricsHandler.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.filters; +package net.frozenorb.apiv3.handlers; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; @@ -6,6 +6,7 @@ import net.frozenorb.apiv3.APIv3; public final class MetricsHandler implements Handler { + @Override public void handle(RoutingContext ctx) { APIv3.getStatsD().incrementCounter("apiv3.http.requests"); ctx.next(); diff --git a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java index fe6a990..49f7065 100644 --- a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java @@ -1,9 +1,9 @@ package net.frozenorb.apiv3.models; import com.google.common.collect.ImmutableMap; +import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actors.Actor; @@ -15,12 +15,8 @@ import net.frozenorb.apiv3.utils.SyncUtils; import org.bson.Document; import org.bson.types.ObjectId; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; -@Entity public final class AuditLogEntry { private static final MongoCollection auditLogCollection = APIv3.getDatabase().getCollection("auditLog", AuditLogEntry.class); @@ -34,22 +30,46 @@ public final class AuditLogEntry { @Getter private AuditLogActionType type; @Getter private Map metadata; - public static List findAll() { + public static List findAllSync() { return SyncUtils.blockMulti(auditLogCollection.find()); } - public static AuditLogEntry findById(String id) { + public static List 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))); } - public static List findByUser(User user) { - return findByUser(user.getId()); + public static List findByUserSync(User user) { + return findByUserSync(user.getId()); } - public static List findByUser(UUID user) { + public static List findByUserSync(UUID user) { return SyncUtils.blockMulti(auditLogCollection.find(new Document("user", user))); } + public static void findAll(SingleResultCallback> callback) { + auditLogCollection.find().into(new ArrayList<>(), callback); + } + + public static void findAllPaginated(int skip, int pageSize, SingleResultCallback> callback) { + auditLogCollection.find().sort(new Document("performedAt", 1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback callback) { + auditLogCollection.find(new Document("_id", id)).first(callback); + } + + public static void findByUser(User user, SingleResultCallback> callback) { + findByUser(user.getId(), callback); + } + + public static void findByUser(UUID user, SingleResultCallback> callback) { + auditLogCollection.find(new Document("user", user)).into(new ArrayList<>(), callback); + } + public AuditLogEntry() {} // For Morphia public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map metadata) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Grant.java b/src/main/java/net/frozenorb/apiv3/models/Grant.java index 7960b16..cda879e 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Grant.java +++ b/src/main/java/net/frozenorb/apiv3/models/Grant.java @@ -1,11 +1,10 @@ package net.frozenorb.apiv3.models; import com.google.common.collect.Collections2; +import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; -import com.mongodb.client.result.UpdateResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; import net.frozenorb.apiv3.APIv3; @@ -16,7 +15,6 @@ import org.bson.types.ObjectId; import java.util.*; -@Entity @AllArgsConstructor public final class Grant { @@ -36,26 +34,74 @@ public final class Grant { @Getter private Date removedAt; @Getter private String removalReason; - public static List findAll() { + public static List findAllSync() { return SyncUtils.blockMulti(grantsCollection.find()); } - public static List findByRank(Iterable ranks) { + public static List findAllPaginatedSync(int skip, int pageSize) { + return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize)); + } + + public static List findByRankSync(Iterable 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))); } - public static List findByUser(User user) { - return findByUser(user.getId()); + public static List findByUserSync(User user) { + return findByUserSync(user.getId()); } - public static List findByUser(UUID user) { + public static List findByUserSync(UUID user) { return SyncUtils.blockMulti(grantsCollection.find(new Document("user", user))); } + public static void findAll(SingleResultCallback> callback) { + grantsCollection.find().into(new ArrayList<>(), callback); + } + + public static void findAllPaginated(int skip, int pageSize, SingleResultCallback> callback) { + grantsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); + } + + public static void findByRank(Iterable ranks, SingleResultCallback> callback) { + grantsCollection.find(new Document("rank", new Document("$in", ranks))).into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback callback) { + grantsCollection.find(new Document("_id", id)).first(callback); + } + + public static void findByUser(User user, SingleResultCallback> callback) { + findByUser(user.getId(), callback); + } + + public static void findByUser(UUID user, SingleResultCallback> callback) { + grantsCollection.find(new Document("user", user)).into(new ArrayList<>(), callback); + } + + public static void findByUserGrouped(Iterable users, SingleResultCallback>> callback) { + grantsCollection.find(new Document("user", new Document("$in", users))).into(new ArrayList<>(), (grants, error) -> { + if (error != null) { + callback.onResult(null, error); + } else { + Map> 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(User user, String reason, Set scopes, Rank rank, Date expiresAt, User addedBy) { diff --git a/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java index 3a52456..98f20d7 100644 --- a/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java @@ -1,8 +1,9 @@ package net.frozenorb.apiv3.models; +import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import com.mongodb.client.result.UpdateResult; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; import net.frozenorb.apiv3.APIv3; @@ -11,11 +12,11 @@ import net.frozenorb.apiv3.utils.SyncUtils; import org.bson.Document; import org.bson.types.ObjectId; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; -@Entity @AllArgsConstructor public final class IPLogEntry { @@ -28,30 +29,54 @@ public final class IPLogEntry { @Getter private Date lastSeenAt; @Getter private int uses; - public static List findAll() { + public static List findAllSync() { 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))); } - public static List findByUser(User user) { - return findByUser(user.getId()); + public static List findByUserSync(User user) { + return findByUserSync(user.getId()); } - public static List findByUser(UUID user) { + public static List findByUserSync(UUID user) { return SyncUtils.blockMulti(ipLogCollection.find(new Document("user", user))); } - public static IPLogEntry findByUserAndIp(User user, String userIp) { - return findByUserAndIp(user.getId(), userIp); + public static IPLogEntry findByUserAndIpSync(User user, String 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))); } + public static void findAll(SingleResultCallback> callback) { + ipLogCollection.find().into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback callback) { + ipLogCollection.find(new Document("_id", id)).first(callback); + } + + public static void findByUser(User user, SingleResultCallback> callback) { + findByUser(user.getId(), callback); + } + + public static void findByUser(UUID user, SingleResultCallback> callback) { + ipLogCollection.find(new Document("user", user)).into(new ArrayList<>(), callback); + } + + public static void findByUserAndIp(User user, String userIp, SingleResultCallback callback) { + findByUserAndIp(user.getId(), userIp, callback); + } + + public static void findByUserAndIp(UUID user, String userIp, SingleResultCallback callback) { + ipLogCollection.find(new Document("user", user).append("userIp", userIp)).first(callback); + } + public IPLogEntry() {} // For Morphia public IPLogEntry(User user, String userIp) { @@ -74,4 +99,10 @@ public final class IPLogEntry { callback.get(); } + public void save() { + BlockingCallback callback = new BlockingCallback<>(); + ipLogCollection.replaceOne(new Document("_id", id), this, callback); + callback.get(); + } + } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java index cf56f6c..8d000c5 100644 --- a/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java @@ -1,21 +1,22 @@ package net.frozenorb.apiv3.models; +import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.BlockingCallback; +import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.utils.SyncUtils; import org.bson.Document; +import java.util.ArrayList; import java.util.List; import java.util.Map; -@Entity public final class NotificationTemplate { private static final MongoCollection notificationTemplatesCollection = APIv3.getDatabase().getCollection("notificationTemplates", NotificationTemplate.class); @@ -24,14 +25,22 @@ public final class NotificationTemplate { @Getter @Setter private String subject; @Getter @Setter private String body; - public static List findAll() { + public static List findAllSync() { 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))); } + public static void findAll(SingleResultCallback> callback) { + notificationTemplatesCollection.find().into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback callback) { + notificationTemplatesCollection.find(new Document("_id", id)).first(callback); + } + public NotificationTemplate() {} // For Morphia public NotificationTemplate(String id, String subject, String body) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Punishment.java b/src/main/java/net/frozenorb/apiv3/models/Punishment.java index 643ce5b..3a42564 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Punishment.java +++ b/src/main/java/net/frozenorb/apiv3/models/Punishment.java @@ -1,9 +1,9 @@ package net.frozenorb.apiv3.models; +import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; import net.frozenorb.apiv3.APIv3; @@ -17,7 +17,6 @@ import org.bson.types.ObjectId; import java.util.*; -@Entity @AllArgsConstructor public final class Punishment { @@ -39,34 +38,90 @@ public final class Punishment { @Getter private Date removedAt; @Getter private String removalReason; - public static List findAll() { + public static List findAllSync() { return SyncUtils.blockMulti(punishmentsCollection.find()); } - public static List findByType(Iterable types) { + public static List findAllPaginatedSync(int skip, int pageSize) { + return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize)); + } + + public static List findByTypeSync(Iterable 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))); } - public static List findByUser(User user) { - return findByUser(user.getId()); + public static List findByUserSync(User user) { + return findByUserSync(user.getId()); } - public static List findByUser(UUID user) { + public static List findByUserSync(UUID user) { return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user))); } - public static List findByUserAndType(User user, Iterable types) { - return findByUserAndType(user.getId(), types); + public static List findByUserAndTypeSync(User user, Iterable types) { + return findByUserAndTypeSync(user.getId(), types); } - public static List findByUserAndType(UUID user, Iterable types) { + public static List findByUserAndTypeSync(UUID user, Iterable types) { return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", types)))); } + public static void findAll(SingleResultCallback> callback) { + punishmentsCollection.find().into(new ArrayList<>(), callback); + } + + public static void findAllPaginated(int skip, int pageSize, SingleResultCallback> callback) { + punishmentsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); + } + + public static void findByType(Iterable types, SingleResultCallback> callback) { + punishmentsCollection.find(new Document("type", new Document("$in", types))).into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback callback) { + punishmentsCollection.find(new Document("_id", id)).first(callback); + } + + public static void findByUser(User user, SingleResultCallback> callback) { + findByUser(user.getId(), callback); + } + + public static void findByUser(UUID user, SingleResultCallback> callback) { + punishmentsCollection.find(new Document("user", user)).into(new ArrayList<>(), callback); + } + + public static void findByUserGrouped(Iterable users, SingleResultCallback>> callback) { + punishmentsCollection.find(new Document("user", new Document("$in", users))).into(new ArrayList<>(), (punishments, error) -> { + if (error != null) { + callback.onResult(null, error); + } else { + Map> 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 types, SingleResultCallback> callback) { + findByUserAndType(user.getId(), types, callback); + } + + public static void findByUserAndType(UUID user, Iterable types, SingleResultCallback> callback) { + punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", types))).into(new ArrayList<>(), callback); + } + public Punishment() {} // For Morphia public Punishment(User user, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map metadata) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Rank.java b/src/main/java/net/frozenorb/apiv3/models/Rank.java index f035a7e..8ec7348 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Rank.java +++ b/src/main/java/net/frozenorb/apiv3/models/Rank.java @@ -5,8 +5,7 @@ import com.google.common.primitives.Ints; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.BlockingCallback; @@ -19,7 +18,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -@Entity public final class Rank { private static final MongoCollection ranksCollection = APIv3.getDatabase().getCollection("ranks", Rank.class); diff --git a/src/main/java/net/frozenorb/apiv3/models/Server.java b/src/main/java/net/frozenorb/apiv3/models/Server.java index b5b9727..d666da9 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Server.java +++ b/src/main/java/net/frozenorb/apiv3/models/Server.java @@ -4,8 +4,7 @@ import com.google.common.collect.ImmutableSet; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; @@ -17,7 +16,6 @@ import org.bson.Document; import java.util.*; import java.util.concurrent.TimeUnit; -@Entity public final class Server { private static final MongoCollection serversCollection = APIv3.getDatabase().getCollection("servers", Server.class); diff --git a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java index 8c5ade2..5d41865 100644 --- a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java @@ -4,8 +4,7 @@ import com.google.gson.annotations.SerializedName; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; @@ -18,7 +17,6 @@ import org.bson.Document; import java.util.*; import java.util.concurrent.TimeUnit; -@Entity public final class ServerGroup { private static final MongoCollection serverGroupsCollection = APIv3.getDatabase().getCollection("serverGroups", ServerGroup.class); @@ -29,8 +27,6 @@ public final class ServerGroup { @Getter @Id private String id; @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 // empty, Morphia will load them as null, not empty sets. @Getter @Setter @ExcludeFromReplies private Set announcements = new HashSet<>(); @@ -48,10 +44,9 @@ public final class ServerGroup { public ServerGroup() {} // For Morphia - public ServerGroup(String id, String image, boolean isPublic) { + public ServerGroup(String id, String image) { this.id = id; this.image = image; - this.isPublic = isPublic; } public Map calculatePermissions(Rank userRank) { diff --git a/src/main/java/net/frozenorb/apiv3/models/User.java b/src/main/java/net/frozenorb/apiv3/models/User.java index 6e27e32..3b6ccb7 100644 --- a/src/main/java/net/frozenorb/apiv3/models/User.java +++ b/src/main/java/net/frozenorb/apiv3/models/User.java @@ -1,18 +1,22 @@ 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.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.hash.Hashing; +import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.UpdateResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; 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.utils.MojangUtils; import net.frozenorb.apiv3.utils.PermissionUtils; @@ -22,13 +26,12 @@ import org.bson.Document; import java.util.*; -@Entity @AllArgsConstructor public final class User { private static final MongoCollection 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 @ExcludeFromReplies private Map aliases = new HashMap<>(); @Getter @ExcludeFromReplies @Setter private String totpSecret; @@ -42,11 +45,11 @@ public final class User { @Getter private Date firstSeenAt; @Getter private boolean online; - public static List findAll() { + public static List findAllSync() { return SyncUtils.blockMulti(usersCollection.find()); } - public static User findById(String id) { + public static User findByIdSync(String id) { UUID uuid; try { @@ -55,10 +58,10 @@ public final class User { return null; } - return findById(uuid); + return findByIdSync(uuid); } - public static User findById(UUID id) { + public static User findByIdSync(UUID id) { if (UUIDUtils.isAcceptableUUID(id)) { return SyncUtils.blockOne(usersCollection.find(new Document("_id", id))); } 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))); } - public static User findByLastUsername(String lastUsername) { + public static User findByLastUsernameSync(String lastUsername) { return SyncUtils.blockOne(usersCollection.find(new Document("lastUsername", lastUsername))); } + public static void findAll(SingleResultCallback> callback) { + usersCollection.find().into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback 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 callback) { + if (UUIDUtils.isAcceptableUUID(id)) { + usersCollection.find(new Document("_id", id)).first(callback); + } else { + callback.onResult(null, null); + } + } + + public static void findByIdGrouped(Iterable search, SingleResultCallback> callback) { + usersCollection.find(new Document("_id", new Document("$in", search))).into(new ArrayList<>(), (users, error) -> { + if (error != null) { + callback.onResult(null, error); + } else { + Map 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 callback) { + usersCollection.find(new Document("emailToken", emailToken)).first(callback); + } + + public static void findByLastUsername(String lastUsername, SingleResultCallback callback) { + usersCollection.find(new Document("lastUsername", lastUsername)).first(callback); + } + public User() {} // For Morphia public User(UUID id, String lastUsername) { @@ -134,8 +186,10 @@ public final class User { User withNewUsername; - while ((withNewUsername = User.findByLastUsername(username)) != null) { - String newUsername = MojangUtils.getName(withNewUsername.getId()); + while ((withNewUsername = User.findByLastUsernameSync(username)) != null) { + BlockingCallback callback = new BlockingCallback<>(); + MojangUtils.getName(withNewUsername.getId(), callback); + String newUsername = callback.get(); withNewUsername.updateUsername(newUsername); } } @@ -164,7 +218,7 @@ public final class User { } public Rank getHighestRankScoped(ServerGroup serverGroup) { - return getHighestRankScoped(serverGroup, Grant.findByUser(this)); + return getHighestRankScoped(serverGroup, Grant.findByUserSync(this)); } // TODO: Clean @@ -195,7 +249,7 @@ public final class User { public Map getHighestRanks() { Map highestRanks = new HashMap<>(); Rank defaultRank = Rank.findById("default"); - List userGrants = Grant.findByUser(this); + List userGrants = Grant.findByUserSync(this); for (ServerGroup serverGroup : ServerGroup.findAll()) { Rank highest = defaultRank; @@ -221,12 +275,12 @@ public final class User { public Map getLoginInfo(Server server) { return createLoginInfo( server, - Punishment.findByUserAndType(this, ImmutableSet.of( + Punishment.findByUserAndTypeSync(this, ImmutableSet.of( Punishment.PunishmentType.BLACKLIST, Punishment.PunishmentType.BAN, Punishment.PunishmentType.MUTE )), - Grant.findByUser(this) + Grant.findByUserSync(this) ); } diff --git a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java index bee4fed..5f1831d 100644 --- a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java @@ -1,11 +1,11 @@ package net.frozenorb.apiv3.models; import com.google.common.collect.ImmutableMap; +import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; -import eu.dozd.mongo.annotation.Entity; -import eu.dozd.mongo.annotation.Id; +import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; @@ -14,11 +14,11 @@ import net.frozenorb.apiv3.utils.SyncUtils; import org.bson.Document; import org.bson.types.ObjectId; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; -@Entity public final class UserMetaEntry { private static final MongoCollection userMetaCollection = APIv3.getDatabase().getCollection("userMeta", UserMetaEntry.class); @@ -28,22 +28,38 @@ public final class UserMetaEntry { @Getter private String serverGroup; @Getter @Setter private Map data; - public static List findAll() { + public static List findAllSync() { 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))); } - public static UserMetaEntry findByUserAndGroup(User user, ServerGroup serverGroup) { - return findByUserAndGroup(user.getId(), serverGroup); + public static UserMetaEntry findByUserAndGroupSync(User user, ServerGroup 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()))); } + public static void findAll(SingleResultCallback> callback) { + userMetaCollection.find().into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback callback) { + userMetaCollection.find(new Document("_id", id)).first(callback); + } + + public static void findByUserAndGroup(User user, ServerGroup serverGroup, SingleResultCallback callback) { + findByUserAndGroup(user.getId(), serverGroup, callback); + } + + public static void findByUserAndGroup(UUID user, ServerGroup serverGroup, SingleResultCallback callback) { + userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())).first(callback); + } + public UserMetaEntry() {} // For Morphia public UserMetaEntry(User user, ServerGroup serverGroup, Map data) { diff --git a/src/main/java/net/frozenorb/apiv3/routes/GETDump.java b/src/main/java/net/frozenorb/apiv3/routes/GETDump.java index 449e054..27629a1 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/GETDump.java +++ b/src/main/java/net/frozenorb/apiv3/routes/GETDump.java @@ -29,8 +29,7 @@ public final class GETDump implements Handler { List banCache = new ArrayList<>(); List blacklistCache = new ArrayList<>(); - Punishment.findByType - (ImmutableSet.of( + Punishment.findByTypeSync(ImmutableSet.of( Punishment.PunishmentType.BAN, Punishment.PunishmentType.BLACKLIST )).forEach((punishment) -> { @@ -52,7 +51,7 @@ public final class GETDump implements Handler { if (tick == 0 || tick % 2 == 0) { Map> grantCache = new HashMap<>(); - Grant.findAll().forEach((grant) -> { + Grant.findAllSync().forEach((grant) -> { if (grant.isActive()) { List users = grantCache.get(grant.getRank()); diff --git a/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java b/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java index 1f3ce08..ea1b639 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java +++ b/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java @@ -10,12 +10,12 @@ public final class GETAuditLog implements Handler { public void handle(RoutingContext ctx) { try { - int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit")); - int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset")); + int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip")); + 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) { - ErrorUtils.respondInvalidInput(ctx, "limit/offset must be numerical inputs."); + ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); } } diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java index 4dc0c67..d96e6bb 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java @@ -13,7 +13,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class DELETEGrant implements Handler { public void handle(RoutingContext ctx) { - Grant grant = Grant.findById(ctx.request().getParam("id")); + Grant grant = Grant.findByIdSync(ctx.request().getParam("id")); if (grant == null) { ErrorUtils.respondNotFound(ctx, "Grant", ctx.request().getParam("id")); @@ -23,7 +23,7 @@ public final class DELETEGrant implements Handler { return; } - User removedBy = User.findById(ctx.request().getParam("removedBy")); + User removedBy = User.findByIdSync(ctx.request().getParam("removedBy")); if (removedBy == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrant.java index d544499..f9cd886 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrant.java @@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.Grant; public final class GETGrant implements Handler { public void handle(RoutingContext ctx) { - APIv3.respondJson(ctx, Grant.findById(ctx.request().getParam("id"))); + APIv3.respondJson(ctx, Grant.findByIdSync(ctx.request().getParam("id"))); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java b/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java index f7120f0..021cdab 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java @@ -10,12 +10,12 @@ public final class GETGrants implements Handler { public void handle(RoutingContext ctx) { try { - int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit")); - int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset")); + int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip")); + 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) { - ErrorUtils.respondInvalidInput(ctx, "limit and offset must be numerical inputs."); + ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); } } diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java b/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java index 36e392a..4313fac 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/GETUserGrants.java @@ -10,14 +10,14 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class GETUserGrants implements Handler { public void handle(RoutingContext ctx) { - User target = User.findById(ctx.request().getParam("id")); + User target = User.findByIdSync(ctx.request().getParam("id")); if (target == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); return; } - APIv3.respondJson(ctx, Grant.findByUser(target)); + APIv3.respondJson(ctx, Grant.findByUserSync(target)); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java index 709bc5f..18976ac 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java @@ -16,7 +16,7 @@ import java.util.Set; public final class POSTUserGrant implements Handler { public void handle(RoutingContext ctx) { - User target = User.findById(ctx.request().getParam("id")); + User target = User.findByIdSync(ctx.request().getParam("id")); if (target == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -67,7 +67,7 @@ public final class POSTUserGrant implements Handler { } // 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.insert(); diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java b/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java index 1addce1..42e5310 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java +++ b/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java @@ -10,14 +10,14 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class GETUserIPLog implements Handler { public void handle(RoutingContext ctx) { - User target = User.findById(ctx.request().getParam("id")); + User target = User.findByIdSync(ctx.request().getParam("id")); if (target == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); return; } - APIv3.respondJson(ctx, IPLogEntry.findByUser(target)); + APIv3.respondJson(ctx, IPLogEntry.findByUserSync(target)); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/DELETENotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/DELETENotificationTemplate.java index 8afc202..da8850f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/DELETENotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/DELETENotificationTemplate.java @@ -9,7 +9,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class DELETENotificationTemplate implements Handler { public void handle(RoutingContext ctx) { - NotificationTemplate notificationTemplate = NotificationTemplate.findById(ctx.request().getParam("id")); + NotificationTemplate notificationTemplate = NotificationTemplate.findByIdSync(ctx.request().getParam("id")); if (notificationTemplate == null) { ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplate.java index 58bf102..8e6632f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplate.java @@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.NotificationTemplate; public final class GETNotificationTemplate implements Handler { public void handle(RoutingContext ctx) { - APIv3.respondJson(ctx, NotificationTemplate.findById(ctx.request().getParam("id"))); + APIv3.respondJson(ctx, NotificationTemplate.findByIdSync(ctx.request().getParam("id"))); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplates.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplates.java index 2dcd2fc..16cdba6 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplates.java +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplates.java @@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.NotificationTemplate; public final class GETNotificationTemplates implements Handler { public void handle(RoutingContext ctx) { - APIv3.respondJson(ctx, NotificationTemplate.findAll()); + APIv3.respondJson(ctx, NotificationTemplate.findAllSync()); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java index c517ea4..33e07bc 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java @@ -13,7 +13,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class DELETEPunishment implements Handler { public void handle(RoutingContext ctx) { - Punishment punishment = Punishment.findById(ctx.request().getParam("id")); + Punishment punishment = Punishment.findByIdSync(ctx.request().getParam("id")); if (punishment == null) { ErrorUtils.respondNotFound(ctx, "Punishment", ctx.request().getParam("id")); @@ -23,7 +23,7 @@ public final class DELETEPunishment implements Handler { return; } - User removedBy = User.findById(ctx.request().getParam("removedBy")); + User removedBy = User.findByIdSync(ctx.request().getParam("removedBy")); if (removedBy == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java index f502ffa..8a61e6f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishment.java @@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.Punishment; public final class GETPunishment implements Handler { public void handle(RoutingContext ctx) { - APIv3.respondJson(ctx, Punishment.findById(ctx.request().getParam("id"))); + APIv3.respondJson(ctx, Punishment.findByIdSync(ctx.request().getParam("id"))); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java index 1e0a41a..28b9109 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java @@ -10,12 +10,12 @@ public final class GETPunishments implements Handler { public void handle(RoutingContext ctx) { try { - int limit = ctx.request().getParam("limit") == null ? 100 : Integer.parseInt(ctx.request().getParam("limit")); - int offset = ctx.request().getParam("offset") == null ? 0 : Integer.parseInt(ctx.request().getParam("offset")); + int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip")); + 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) { - ErrorUtils.respondInvalidInput(ctx, "limit and offset must be numerical inputs."); + ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); } } diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java index f55ace0..15a7842 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java @@ -10,14 +10,14 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class GETUserPunishments implements Handler { public void handle(RoutingContext ctx) { - User target = User.findById(ctx.request().getParam("id")); + User target = User.findByIdSync(ctx.request().getParam("id")); if (target == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); return; } - APIv3.respondJson(ctx, Punishment.findByUser(target)); + APIv3.respondJson(ctx, Punishment.findByUserSync(target)); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java index 8cb4dd9..bd2ef45 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java @@ -17,7 +17,7 @@ import java.util.Map; public final class POSTUserPunish implements Handler { public void handle(RoutingContext ctx) { - User target = User.findById(ctx.request().getParam("id")); + User target = User.findByIdSync(ctx.request().getParam("id")); if (target == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -34,9 +34,9 @@ public final class POSTUserPunish implements Handler { Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type")); 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()) { - 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; } } @@ -63,7 +63,7 @@ public final class POSTUserPunish implements Handler { } // 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)) { ErrorUtils.respondGeneric(ctx, target.getLastSeenOn() + " is protected from punishments."); diff --git a/src/main/java/net/frozenorb/apiv3/routes/serverGroups/POSTServerGroup.java b/src/main/java/net/frozenorb/apiv3/routes/serverGroups/POSTServerGroup.java index 72de2c4..b6ba7b0 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/serverGroups/POSTServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/routes/serverGroups/POSTServerGroup.java @@ -10,9 +10,8 @@ public final class POSTServerGroup implements Handler { public void handle(RoutingContext ctx) { String id = ctx.request().getParam("id"); 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(); APIv3.respondJson(ctx, serverGroup); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java index 2a34f3e..341accd 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java +++ b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java @@ -1,6 +1,9 @@ package net.frozenorb.apiv3.routes.servers; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import lombok.extern.slf4j.Slf4j; @@ -18,7 +21,6 @@ import java.util.*; @Slf4j public final class POSTServerHeartbeat implements Handler { - @SuppressWarnings("unchecked") public void handle(RoutingContext ctx) { Actor actor = ctx.get("actor"); @@ -27,57 +29,132 @@ public final class POSTServerHeartbeat implements Handler { return; } - Server actorServer = Server.byId(actor.getName()); - ServerGroup actorServerGroup = ServerGroup.byId(actorServer.getServerGroup()); + Server actorServer = Server.findById(actor.getName()); + ServerGroup actorServerGroup = ServerGroup.findById(actorServer.getServerGroup()); Document reqJson = Document.parse(ctx.getBodyAsString()); - Map onlinePlayersNames = new HashMap<>(); - Map onlinePlayersUsers; - Map> onlinePlayersGrants; - Map> onlinePlayersPunishments; - Map playersResponse = new HashMap<>(); + Map playerNames = extractPlayerNames(reqJson); - // This code is messy, but we have to do db ops in parallel to avoid - // spamming Mongo with queries, so we do this. - for (Object player : (List) reqJson.get("players")) { - Document playerJson = (Document) player; - UUID uuid = UUID.fromString(playerJson.getString("uuid")); - String username = playerJson.getString("username"); - - if (UUIDUtils.isAcceptableUUID(uuid)) { - onlinePlayersNames.put(uuid, username); + CompositeFuture.all( + createInfoResponse(actorServer, reqJson.getDouble("tps"), playerNames), + createPlayerResponse(actorServer, playerNames), + createPermissionsResponse(actorServerGroup), + createEventsResponse((List) reqJson.get("events")) + ).setHandler((result) -> { + if (result.succeeded()) { + APIv3.respondJson(ctx, ImmutableMap.of( + "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()); } + }); + } + + public Future> createInfoResponse(Server server, double tps, Map playerNames) { + Future> callback = Future.future(); + + server.setPlayers(playerNames.keySet()); + server.setLastTps(tps); + server.setLastUpdatedAt(new Date()); + server.save(); + + return callback; + } + + public Future> createPlayerResponse(Server server, Map playerNames) { + Future> callback = Future.future(); + + Future> userLookupCallback = Future.future(); + Future>> grantLookupCallback = Future.future(); + Future>> 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 users = result.result().result(0); + Map> grants = result.result().result(1); + Map> punishments = result.result().result(2); + Map response = new HashMap<>(); + + for (Map.Entry userEntry : users.entrySet()) { + UUID uuid = userEntry.getKey(); + User user = userEntry.getValue(); + + if (user == null) { + String username = playerNames.get(uuid); + user = new User(uuid, username); + user.insert(); + users.put(uuid, user); + } + + // Only save if needed + if (user.seenOnServer(server)) { + user.save(); + } + + response.put(uuid.toString(), user.createLoginInfo(server, punishments.get(uuid), grants.get(uuid))); + } + + callback.complete(response); + } + }); + + return callback; + } + + public Future> createPermissionsResponse(ServerGroup serverGroup) { + Future> callback = Future.future(); + Map permissionsResponse = new HashMap<>(); + + for (Rank rank : Rank.findAll()) { + Map scopedPermissions = PermissionUtils.mergePermissions( + PermissionUtils.getDefaultPermissions(rank), + serverGroup.calculatePermissions(rank) + ); + + permissionsResponse.put(rank.getId(), scopedPermissions); } - onlinePlayersUsers = User.byIdGrouped(onlinePlayersNames.keySet()); + callback.complete(permissionsResponse); + return callback; + } - for (Map.Entry entry : new HashMap<>(onlinePlayersUsers).entrySet()) { - UUID uuid = entry.getKey(); - User user = entry.getValue(); + public Future> createEventsResponse(List eventsData) { + Future> callback = Future.future(); - if (user == null) { - // Will be saved in the User constructor - String username = onlinePlayersNames.get(uuid); - user = new User(uuid, username); - onlinePlayersUsers.put(uuid, user); - } - - // Only save if needed - if (user.seenOnServer(actorServer)) { - APIv3.getDatastore().save(user); - } - } - - onlinePlayersGrants = Grant.byUserGrouped(onlinePlayersUsers.keySet()); - onlinePlayersPunishments = Punishment.byUserGrouped(onlinePlayersUsers.keySet()); - - for (Map.Entry 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) reqJson.get("events")) { + for (Object event : eventsData) { Document eventJson = (Document) event; String type = eventJson.getString("type"); @@ -93,26 +170,24 @@ public final class POSTServerHeartbeat implements Handler { } } - Map> permissionsResponse = new HashMap<>(); + callback.complete(ImmutableMap.of()); + return callback; + } - for (Rank rank : Rank.findAll()) { - Map scopedPermissions = PermissionUtils.mergePermissions( - PermissionUtils.getDefaultPermissions(rank), - actorServerGroup.calculatePermissions(rank) - ); + public Map extractPlayerNames(Document reqJson) { + Map result = new HashMap<>(); - permissionsResponse.put(rank.getId(), scopedPermissions); + for (Object player : (List) reqJson.get("players")) { + Document playerJson = (Document) player; + UUID uuid = UUID.fromString(playerJson.getString("uuid")); + String username = playerJson.getString("username"); + + if (UUIDUtils.isAcceptableUUID(uuid)) { + result.put(uuid, username); + } } - actorServer.setPlayers(onlinePlayersNames.keySet()); - actorServer.setLastTps(reqJson.getDouble("lastTps")); - actorServer.setLastUpdatedAt(new Date()); - actorServer.save(); - - APIv3.respondJson(ctx, ImmutableMap.of( - "players", playersResponse, - "permissions", permissionsResponse - )); + return result; } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java index 03716b5..3593415 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserMeta.java @@ -12,7 +12,7 @@ import org.bson.Document; public final class DELETEUserMeta implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -26,7 +26,7 @@ public final class DELETEUserMeta implements Handler { return; } - UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroup(user, serverGroup); + UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup); if (userMetaEntry != null) { userMetaEntry.delete(); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java index f35b6cb..b2f8a50 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java @@ -14,7 +14,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class DELETEUserPunishment implements Handler { public void handle(RoutingContext ctx) { - User target = User.findById(ctx.request().getParam("id")); + User target = User.findByIdSync(ctx.request().getParam("id")); if (target == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -22,7 +22,7 @@ public final class DELETEUserPunishment implements Handler { } 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) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); @@ -36,7 +36,7 @@ public final class DELETEUserPunishment implements Handler { return; } - for (Punishment punishment : target.getPunishments(ImmutableSet.of(type))) { + for (Punishment punishment : Punishment.findByUserAndTypeSync(target, ImmutableSet.of(type))) { if (punishment.isActive()) { punishment.delete(removedBy, reason); AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of()); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java index 3a5a398..7a43f48 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java @@ -27,9 +27,9 @@ public final class GETStaff implements Handler { return Integer.compare(firstRank.getWeight(), secondRank.getWeight()); }); - Grant.findByRank(staffRanks.values()).forEach(grant -> { + Grant.findByRankSync(staffRanks.values()).forEach(grant -> { if (grant.isActive()) { - User user = User.findById(grant.getUser()); + User user = User.findByIdSync(grant.getUser()); Rank rank = staffRanks.get(grant.getRank()); if (!result.containsKey(rank.getId())) { diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java index a0065e0..0684633 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUser.java @@ -8,7 +8,7 @@ import net.frozenorb.apiv3.models.User; public final class GETUser implements Handler { public void handle(RoutingContext ctx) { - APIv3.respondJson(ctx, User.findById(ctx.request().getParam("id"))); + APIv3.respondJson(ctx, User.findByIdSync(ctx.request().getParam("id"))); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java index 4283ba3..d870bfa 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java @@ -13,7 +13,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class GETUserDetails implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -23,9 +23,9 @@ public final class GETUserDetails implements Handler { // Too many fields to use .of() APIv3.respondJson(ctx, ImmutableMap.builder() .put("user", user) - .put("grants", Grant.findByUser(user)) - .put("ipLog", IPLogEntry.findByUser(user)) - .put("punishments", Punishment.findByUser(user)) + .put("grants", Grant.findByUserSync(user)) + .put("ipLog", IPLogEntry.findByUserSync(user)) + .put("punishments", Punishment.findByUserSync(user)) .put("aliases", user.getAliases()) .put("totpSetup", user.getTotpSecret() != null) .build() diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java index 948dc60..678d4fb 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserMeta.java @@ -11,7 +11,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class GETUserMeta implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -25,7 +25,7 @@ public final class GETUserMeta implements Handler { return; } - UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroup(user, serverGroup); + UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup); APIv3.respondJson(ctx, userMetaEntry.getData()); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java index fefe302..40de6aa 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java @@ -2,9 +2,11 @@ package net.frozenorb.apiv3.routes.users; import com.google.common.collect.ImmutableMap; import io.vertx.core.Handler; +import io.vertx.core.cli.converters.BooleanConverter; import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.TOTPUtils; @@ -12,7 +14,7 @@ import net.frozenorb.apiv3.utils.TOTPUtils; public final class GETUserRequiresTOTP implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -34,7 +36,10 @@ public final class GETUserRequiresTOTP implements Handler { return; } - if (TOTPUtils.isPreAuthorized(user, userIp)) { + BlockingCallback preAuthorizedCallback = new BlockingCallback<>(); + TOTPUtils.isPreAuthorized(user, userIp, preAuthorizedCallback); + + if (preAuthorizedCallback.get()) { APIv3.respondJson(ctx, ImmutableMap.of( "required", false, "message", "User's IP has already been validated" diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java index fa362ab..c710f5e 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserVerifyPassword.java @@ -10,7 +10,7 @@ import net.frozenorb.apiv3.utils.ErrorUtils; public final class GETUserVerifyPassword implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java index 39242a2..cfd569e 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java @@ -22,7 +22,7 @@ public final class POSTUserConfirmRegister implements Handler { "nicole chelsea biteme matthew access yankees 987654321 dallas austin thunder taylor matrix").split(" ")); public void handle(RoutingContext ctx) { - User user = User.findByEmailToken(ctx.request().getParam("emailToken")); + User user = User.findByEmailTokenSync(ctx.request().getParam("emailToken")); if (user == null) { ErrorUtils.respondNotFound(ctx, "Email token", ctx.request().getParam("emailToken")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLeave.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLeave.java index 802c71c..23285d2 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLeave.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLeave.java @@ -10,7 +10,7 @@ public class POSTUserLeave implements Handler { @Override public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java index 05264fc..2a01d7e 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java @@ -5,6 +5,7 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.ActorType; +import net.frozenorb.apiv3.models.IPLogEntry; import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.utils.ErrorUtils; @@ -23,7 +24,7 @@ public final class POSTUserLogin implements Handler { return; } - User user = User.findById(uuid); + User user = User.findByIdSync(uuid); String username = ctx.request().getParam("username"); String userIp = ctx.request().getParam("userIp"); Actor actor = ctx.get("actor"); @@ -45,7 +46,19 @@ public final class POSTUserLogin implements Handler { 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); APIv3.respondJson(ctx, user.getLoginInfo(actorServer)); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java index f9454b7..65ebc15 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java @@ -6,6 +6,7 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.NotificationTemplate; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.utils.ErrorUtils; @@ -15,7 +16,7 @@ import java.util.Map; public final class POSTUserNotify implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -27,7 +28,7 @@ public final class POSTUserNotify implements Handler { return; } - NotificationTemplate template = NotificationTemplate.findById(ctx.request().getParam("template")); + NotificationTemplate template = NotificationTemplate.findByIdSync(ctx.request().getParam("template")); if (template == null) { ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("template")); @@ -49,7 +50,10 @@ public final class POSTUserNotify implements Handler { try { Notification notification = new Notification(template, subjectReplacements, bodyReplacements); - notification.sendAsEmail(user.getEmail()); + BlockingCallback callback = new BlockingCallback<>(); + notification.sendAsEmail(user.getEmail(), callback); + callback.get(); + APIv3.respondJson(ctx, ImmutableMap.of( "success", true )); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java index 3e3aad3..6aec42b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java @@ -6,6 +6,7 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.NotificationTemplate; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.utils.ErrorUtils; @@ -24,7 +25,7 @@ public final class POSTUserRegister implements Handler { ); public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -59,10 +60,12 @@ public final class POSTUserRegister implements Handler { "emailToken", user.getEmailToken() ); - Notification notification = new Notification(NotificationTemplate.findById("email-confirmation"), replacements, replacements); + Notification notification = new Notification(NotificationTemplate.findByIdSync("email-confirmation"), replacements, replacements); try { - notification.sendAsEmail(user.getEmail()); + BlockingCallback callback = new BlockingCallback<>(); + notification.sendAsEmail(user.getEmail(), callback); + callback.get(); APIv3.respondJson(ctx, ImmutableMap.of( "success", true )); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserSetupTOTP.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserSetupTOTP.java index 9b9f2bc..1725563 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserSetupTOTP.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserSetupTOTP.java @@ -12,7 +12,7 @@ import net.frozenorb.apiv3.utils.TOTPUtils; public final class POSTUserSetupTOTP implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserVerifyTOTP.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserVerifyTOTP.java index bc27b48..82964e6 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserVerifyTOTP.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserVerifyTOTP.java @@ -2,9 +2,12 @@ package net.frozenorb.apiv3.routes.users; import com.google.common.collect.ImmutableMap; import io.vertx.core.Handler; +import io.vertx.core.cli.converters.BooleanConverter; import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.ErrorUtils; import net.frozenorb.apiv3.utils.IPUtils; import net.frozenorb.apiv3.utils.TOTPUtils; @@ -14,7 +17,7 @@ import java.util.concurrent.TimeUnit; public final class POSTUserVerifyTOTP implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -34,8 +37,10 @@ public final class POSTUserVerifyTOTP implements Handler { } int providedCode = Integer.parseInt(ctx.request().getParam("code")); + BlockingCallback recentlyUsedCallback = new BlockingCallback<>(); + TOTPUtils.wasRecentlyUsed(user, providedCode, recentlyUsedCallback); - if (TOTPUtils.wasRecentlyUsed(user, providedCode)) { + if (recentlyUsedCallback.get()) { APIv3.respondJson(ctx, ImmutableMap.of( "authorized", false, "message", "TOTP code was recently used." @@ -46,8 +51,13 @@ public final class POSTUserVerifyTOTP implements Handler { boolean authorized = TOTPUtils.authorizeUser(user, providedCode); if (authorized) { - TOTPUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS); - TOTPUtils.markRecentlyUsed(user, providedCode); + BlockingCallback markPreAuthorizedCallback = new BlockingCallback<>(); + TOTPUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS, markPreAuthorizedCallback); + markPreAuthorizedCallback.get(); + + BlockingCallback markRecentlyUsedCallback = new BlockingCallback<>(); + TOTPUtils.markRecentlyUsed(user, providedCode, markRecentlyUsedCallback); + markRecentlyUsedCallback.get(); APIv3.respondJson(ctx, ImmutableMap.of( "authorized", true, diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java b/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java index 3fa80a8..f18d2e7 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java @@ -5,13 +5,14 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.models.UserMetaEntry; import net.frozenorb.apiv3.utils.ErrorUtils; import org.bson.Document; public final class PUTUserMeta implements Handler { public void handle(RoutingContext ctx) { - User user = User.findById(ctx.request().getParam("id")); + User user = User.findByIdSync(ctx.request().getParam("id")); if (user == null) { ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); @@ -26,8 +27,16 @@ public final class PUTUserMeta implements Handler { } 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); } diff --git a/src/main/java/net/frozenorb/apiv3/serialization/MineHQCodecProvider.java b/src/main/java/net/frozenorb/apiv3/serialization/MineHQCodecProvider.java new file mode 100644 index 0000000..3f405aa --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/serialization/MineHQCodecProvider.java @@ -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 Codec get(Class clazz, CodecRegistry codecRegistry) { + if (clazz == UUID.class) { + return (Codec) new Codec() { + + @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 getEncoderClass() { + return UUID.class; + } + + }; + } else { + return null; + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/serialization/ObjectIdTypeAdapter.java b/src/main/java/net/frozenorb/apiv3/serialization/ObjectIdTypeAdapter.java deleted file mode 100644 index 78337fa..0000000 --- a/src/main/java/net/frozenorb/apiv3/serialization/ObjectIdTypeAdapter.java +++ /dev/null @@ -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 { - - 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(); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonDeserializer.java b/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonDeserializer.java new file mode 100644 index 0000000..155737a --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonDeserializer.java @@ -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 { + + @Override + public UUID deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + return UUID.fromString(jsonParser.getValueAsString()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonSerializer.java b/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonSerializer.java new file mode 100644 index 0000000..5f18aa4 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonSerializer.java @@ -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 { + + @Override + public void serialize(UUID uuid, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { + jsonGenerator.writeString(uuid.toString()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/BlockingCallback.java b/src/main/java/net/frozenorb/apiv3/unsorted/BlockingCallback.java index 939de9c..5db6d1b 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/BlockingCallback.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/BlockingCallback.java @@ -9,6 +9,7 @@ public final class BlockingCallback implements SingleResultCallback { private final SettableFuture future = SettableFuture.create(); + @Override public void onResult(T val, Throwable error) { if (error != null) { future.setException(error); diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java index 1813cba..08ea50b 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java @@ -5,6 +5,7 @@ import com.cribbstechnologies.clients.mandrill.model.MandrillHtmlMessage; import com.cribbstechnologies.clients.mandrill.model.MandrillMessageRequest; import com.cribbstechnologies.clients.mandrill.model.MandrillRecipient; import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest; +import com.mongodb.async.SingleResultCallback; import com.twilio.sdk.TwilioRestException; import com.twilio.sdk.resource.factory.MessageFactory; import net.frozenorb.apiv3.APIv3; @@ -32,7 +33,7 @@ public final class Notification { this.body = template.fillBody(bodyReplacements); } - public void sendAsEmail(String email) throws IOException { + public void sendAsEmail(String email, SingleResultCallback callback) throws IOException { MandrillHtmlMessage message = new MandrillHtmlMessage(); message.setFrom_email("no-reply@minehq.com"); @@ -43,31 +44,49 @@ public final class Notification { new MandrillRecipient(null, email) }); - try { - MandrillMessageRequest request = new MandrillMessageRequest(); - request.setMessage(message); - mandrillMessagesRequest.sendMessage(request); - APIv3.getStatsD().incrementCounter("apiv3.notification.email.success"); - } catch (RequestFailedException ex) { - APIv3.getStatsD().incrementCounter("apiv3.notification.email.failure"); - throw new IOException("Failed to send notification to user", ex); - } + APIv3.getVertxInstance().executeBlocking((future) -> { + try { + MandrillMessageRequest request = new MandrillMessageRequest(); + request.setMessage(message); + mandrillMessagesRequest.sendMessage(request); + APIv3.getStatsD().incrementCounter("apiv3.notification.email.success"); + future.succeeded(); + } catch (RequestFailedException ex) { + APIv3.getStatsD().incrementCounter("apiv3.notification.email.failure"); + 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 callback) throws IOException { List params = new ArrayList<>(); params.add(new BasicNameValuePair("To", phoneNumber)); params.add(new BasicNameValuePair("From", "+13108795180")); params.add(new BasicNameValuePair("Body", body)); - try { - twillioMessageFactory.create(params); - APIv3.getStatsD().incrementCounter("apiv3.notification.text.success"); - } catch (TwilioRestException ex) { - APIv3.getStatsD().incrementCounter("apiv3.notification.text.failure"); - throw new IOException("Failed to send notification to user", ex); - } + APIv3.getVertxInstance().executeBlocking((future) -> { + try { + twillioMessageFactory.create(params); + APIv3.getStatsD().incrementCounter("apiv3.notification.text.success"); + future.succeeded(); + } catch (TwilioRestException ex) { + APIv3.getStatsD().incrementCounter("apiv3.notification.text.failure"); + 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()); + } + }); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java b/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java index 15708bf..5f711c9 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java @@ -6,5 +6,6 @@ import lombok.experimental.UtilityClass; public class Permissions { public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected"; + public static final String SIGN_API_REQUEST = "apiv3.signRequest"; } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java b/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java index fd25359..f297395 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java @@ -11,12 +11,6 @@ import java.util.UUID; @UtilityClass public class MojangUtils { - public static String getName(UUID id) { - BlockingCallback callback = new BlockingCallback<>(); - getName(id, callback); - return callback.get(); - } - public static void getName(UUID id, SingleResultCallback callback) { APIv3.getHttpClient().get("sessionserver.mojang.com", "session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> { response.bodyHandler((body) -> { diff --git a/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java b/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java index 5af89ef..1f8b6b3 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java @@ -1,15 +1,18 @@ package net.frozenorb.apiv3.utils; +import com.mongodb.async.SingleResultCallback; import com.warrenstrange.googleauth.GoogleAuthenticator; import com.warrenstrange.googleauth.GoogleAuthenticatorConfig; import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator; +import io.vertx.redis.RedisClient; +import io.vertx.redis.RedisOptions; import lombok.experimental.UtilityClass; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.User; -import redis.clients.jedis.Jedis; import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; @UtilityClass public class TOTPUtils { @@ -38,34 +41,60 @@ public class TOTPUtils { ); } - public static boolean isPreAuthorized(User user, String ip) { - try (Jedis redis = APIv3.getRedisPool().getResource()) { - return redis.exists(user.getId() + ":preAuthorizedIP:" + ip.toLowerCase()); - } + public static void isPreAuthorized(User user, String ip, SingleResultCallback callback) { + APIv3.getRedisClient().exists(user.getId() + ":preAuthorizedIP:" + ip.toLowerCase(), (result) -> { + 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) { - try (Jedis redis = APIv3.getRedisPool().getResource()) { - String key = user.getId() + ":preAuthorizedIP:" + ip.toLowerCase(); + public static void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback callback) { + String key = user.getId() + ":preAuthorizedIP:" + ip.toLowerCase(); - redis.set(key, ""); - redis.expire(key, (int) unit.toSeconds(duration)); - } + APIv3.getRedisClient().set(key, "", (result) -> { + 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) { - try (Jedis redis = APIv3.getRedisPool().getResource()) { - return redis.exists(user.getId() + ":recentTOTPCodes:" + code); - } + public static void wasRecentlyUsed(User user, int code, SingleResultCallback callback) { + APIv3.getRedisClient().exists(user.getId() + ":recentTOTPCodes:" + code, (result) -> { + if (result.succeeded()) { + callback.onResult(result.result() == 1 , null); + } else { + callback.onResult(null, result.cause()); + } + }); } - public static void markRecentlyUsed(User user, int code) { - try (Jedis redis = APIv3.getRedisPool().getResource()) { - String key = user.getId() + ":recentTOTPCodes:" + code; + public static void markRecentlyUsed(User user, int code, SingleResultCallback callback) { + String key = user.getId() + ":recentTOTPCodes:" + code; - redis.set(key, ""); - redis.expire(key, (int) TimeUnit.MINUTES.toSeconds(5)); - } + APIv3.getRedisClient().set(key, "", (result) -> { + 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()); + } + }); } } \ No newline at end of file