diff --git a/apiv3.properties b/apiv3.properties index 74192b2..a00fa41 100644 --- a/apiv3.properties +++ b/apiv3.properties @@ -7,8 +7,6 @@ mongo.password= redis.address=localhost redis.port=6379 http.port=80 -twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740 -twillio.authToken=982592505a171d3be6b0722f5ecacc0e mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ bugsnag.apiKey=0e47fba8b825416b7cbc839066184509 auth.websiteApiKey=RVbp4hY6sCFVaf diff --git a/pom.xml b/pom.xml index 729b473..f691e55 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ org.apache.maven.plugins maven-jar-plugin + 3.0.1 @@ -33,9 +34,9 @@ 2.3 - - *:* - + + org.projectlombok:lombok + @@ -108,18 +109,6 @@ 2.6.0 - - - com.cribbstechnologies.clients - mandrillClient - 1.1 - - - com.twilio.sdk - twilio-java-sdk - 6.3.0 - - com.warrenstrange diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index 08a02e2..062bdd7 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -7,56 +7,49 @@ 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.common.net.MediaType; 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.client.model.IndexModel; import com.mongodb.connection.ClusterSettings; -import com.timgroup.statsd.NonBlockingStatsDClient; -import com.timgroup.statsd.StatsDClient; 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.HttpHeaders; +import io.vertx.core.http.HttpMethod; 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.LoggerFormat; 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.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; import net.frozenorb.apiv3.routes.announcements.GETAnnouncements; +import net.frozenorb.apiv3.routes.announcements.PUTAnnouncements; import net.frozenorb.apiv3.routes.auditLog.GETAuditLog; +import net.frozenorb.apiv3.routes.auditLog.POSTUserAuditLogEntry; import net.frozenorb.apiv3.routes.chatFilterList.GETChatFilterList; import net.frozenorb.apiv3.routes.grants.*; -import net.frozenorb.apiv3.routes.ipLog.GETUserIPLog; -import net.frozenorb.apiv3.routes.notificationTemplate.DELETENotificationTemplate; -import net.frozenorb.apiv3.routes.notificationTemplate.GETNotificationTemplate; -import net.frozenorb.apiv3.routes.notificationTemplate.GETNotificationTemplates; -import net.frozenorb.apiv3.routes.notificationTemplate.POSTNotificationTemplate; +import net.frozenorb.apiv3.routes.ipBans.*; +import net.frozenorb.apiv3.routes.ipLog.GETUserIpLog; +import net.frozenorb.apiv3.routes.notificationTemplates.DELETENotificationTemplate; +import net.frozenorb.apiv3.routes.notificationTemplates.GETNotificationTemplate; +import net.frozenorb.apiv3.routes.notificationTemplates.GETNotificationTemplates; +import net.frozenorb.apiv3.routes.notificationTemplates.POSTNotificationTemplate; import net.frozenorb.apiv3.routes.punishments.*; import net.frozenorb.apiv3.routes.ranks.DELETERank; import net.frozenorb.apiv3.routes.ranks.GETRank; @@ -68,26 +61,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.*; -import net.frozenorb.apiv3.unsorted.BlockingCallback; +import net.frozenorb.apiv3.serialization.gson.DateTypeAdapter; +import net.frozenorb.apiv3.serialization.gson.FollowAnnotationExclusionStrategy; +import net.frozenorb.apiv3.serialization.jackson.UUIDJsonDeserializer; +import net.frozenorb.apiv3.serialization.jackson.UUIDJsonSerializer; +import net.frozenorb.apiv3.serialization.mongodb.UUIDCodecProvider; 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 java.io.FileInputStream; +import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Field; import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; @Slf4j public final class APIv3 extends AbstractVerticle { @@ -96,38 +86,39 @@ public final class APIv3 extends AbstractVerticle { @Getter private static MongoDatabase database; @Getter private static Properties config = new Properties(); @Getter private static RedisClient redisClient; - @Getter private static StatsDClient statsD; - @Getter private static Vertx vertxInstance; - @Getter private static final Gson gson = new GsonBuilder() + private static final Gson gson = new GsonBuilder() .registerTypeAdapter(Date.class, new DateTypeAdapter()) .setExclusionStrategies(new FollowAnnotationExclusionStrategy()) .create(); @Override public void start() { - vertxInstance = vertx; - setupConfig(); setupDatabase(); setupRedis(); - setupMetrics(); setupBugsnag(); - //setupHttpServer(); + setupHttpServer(); setupHttpClient(); - convertData("mongodb://158.69.126.126", true); + /*V2Converter converter = new V2Converter("mongodb://158.69.126.126", "minehq"); + + converter.startConversion((ignored, error) -> { + if (error != null) { + error.printStackTrace(); + } + });*/ } private void setupConfig() { try (InputStream in = new FileInputStream("apiv3.properties")) { config.load(in); - } catch (Exception ex) { + } catch (IOException ex) { throw new RuntimeException(ex); } } private void setupDatabase() { - ImmutableList credentials = ImmutableList.of(); + List credentials = ImmutableList.of(); if (!config.getProperty("mongo.username").isEmpty()) { credentials = ImmutableList.of( @@ -146,8 +137,8 @@ public final class APIv3 extends AbstractVerticle { List providers = new ArrayList<>(); - // Our override codec - providers.add(new MineHQCodecProvider()); + // Our fixed uuid codec + providers.add(new UUIDCodecProvider()); // Normal providers providers.add(new ValueCodecProvider()); @@ -173,9 +164,34 @@ public final class APIv3 extends AbstractVerticle { .clusterSettings(clusterSettings) .build(); - MongoClient client = MongoClients.create(settings); - database = client.getDatabase(config.getProperty("mongo.database")); - // TODO: Indexes + database = MongoClients.create(settings).getDatabase(config.getProperty("mongo.database")); + database.getCollection("auditLog").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("performedAt", 1)), + new IndexModel(new Document("type", 1)) + ), (a, b) -> {}); + database.getCollection("grants").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("rank", 1)), + new IndexModel(new Document("addedAt", 1)) + ), (a, b) -> {}); + database.getCollection("ipLog").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("user", 1).append("userIp", 1)) + ), (a, b) -> {}); + database.getCollection("punishments").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("type", 1)), + new IndexModel(new Document("addedAt", 1)), + new IndexModel(new Document("addedBy", 1)) + ), (a, b) -> {}); + database.getCollection("users").createIndexes(ImmutableList.of( + new IndexModel(new Document("lastUsername", 1)), + new IndexModel(new Document("emailToken", 1)) + ), (a, b) -> {}); + database.getCollection("userMeta").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1).append("serverGroup", 1)) + ), (a, b) -> {}); } private void setupRedis() { @@ -187,20 +203,6 @@ public final class APIv3 extends AbstractVerticle { ); } - private void setupMetrics() { - statsD = new NonBlockingStatsDClient(null, "localhost", 8125); - - new Timer("Metrics Task").scheduleAtFixedRate(new TimerTask() { - - @Override - public void run() { - statsD.recordGaugeValue("apiv3.memory.usage", Runtime.getRuntime().totalMemory() / (1024 * 1024)); - statsD.recordGaugeValue("apiv3.memory.max", Runtime.getRuntime().maxMemory() / (1024 * 1024)); - } - - }, TimeUnit.SECONDS.toMillis(5), TimeUnit.SECONDS.toMillis(5)); - } - private void setupBugsnag() { Client bugsnag = new Client(config.getProperty("bugsnag.apiKey")); bugsnag.setReleaseStage(config.getProperty("general.releaseStage")); @@ -208,29 +210,40 @@ public final class APIv3 extends AbstractVerticle { bugsnag.setLogger(new BugsnagSLF4JLogger()); } + // TODO: blockingHandler -> handler private void setupHttpServer() { HttpServer webServer = vertx.createHttpServer(); Router mainRouter = Router.router(vertx); - mainRouter.route().handler(new MetricsHandler()); mainRouter.route().handler(new ActorAttributeHandler()); mainRouter.route().handler(new AuthorizationHandler()); - mainRouter.route().handler(LoggerHandler.create()); - mainRouter.route().handler(BodyHandler.create()); + mainRouter.route().handler(LoggerHandler.create(LoggerFormat.TINY)); + mainRouter.route().method(HttpMethod.PUT).method(HttpMethod.POST).handler(BodyHandler.create()); // TODO: The commented out routes - mainRouter.post("/metrics").blockingHandler(new POSTMetrics()); - mainRouter.get("/announcements").blockingHandler(new GETAnnouncements()); + mainRouter.get("/announcements/:id").blockingHandler(new GETAnnouncements()); + mainRouter.put("/announcements/:id").blockingHandler(new PUTAnnouncements()); + mainRouter.get("/auditLog").blockingHandler(new GETAuditLog()); - mainRouter.get("/chatFilterList").blockingHandler(new GETChatFilterList()); - mainRouter.get("/dump/:type").blockingHandler(new GETDump()); - mainRouter.get("/whoami").blockingHandler(new GETWhoAmI()); + mainRouter.post("/user/:id/auditLogEntry").blockingHandler(new POSTUserAuditLogEntry()); + + mainRouter.get("/chatFilterList").handler(new GETChatFilterList()); mainRouter.get("/grant/:id").blockingHandler(new GETGrant()); mainRouter.get("/grants").blockingHandler(new GETGrants()); + mainRouter.get("/user/:id/grants").blockingHandler(new GETUserGrants()); + mainRouter.post("/user/:id/grant").blockingHandler(new POSTUserGrant()); mainRouter.delete("/grant/:id").blockingHandler(new DELETEGrant()); + mainRouter.get("/ipBan/:id").blockingHandler(new GETIpBan()); + mainRouter.get("/ipBans").blockingHandler(new GETIpBans()); + mainRouter.get("/ip/:id/ipBans").blockingHandler(new GETIpIpBans()); + mainRouter.post("/ip/:id/ipBan").blockingHandler(new POSTIpIpBan()); + mainRouter.delete("/ipBan/:id").blockingHandler(new DELETEIpBan()); + + mainRouter.get("/user/:id/ipLog").blockingHandler(new GETUserIpLog()); + mainRouter.get("/notificationTemplate/:id").blockingHandler(new GETNotificationTemplate()); mainRouter.get("/notificationTemplates").blockingHandler(new GETNotificationTemplates()); mainRouter.post("/notificationTemplate").blockingHandler(new POSTNotificationTemplate()); @@ -239,67 +252,49 @@ public final class APIv3 extends AbstractVerticle { mainRouter.get("/punishment/:id").blockingHandler(new GETPunishment()); mainRouter.get("/punishments").blockingHandler(new GETPunishments()); + mainRouter.get("/user/:id/punishments").blockingHandler(new GETUserPunishments()); + mainRouter.post("/user/:id/punish").blockingHandler(new POSTUserPunish()); mainRouter.delete("/punishment/:id").blockingHandler(new DELETEPunishment()); + mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment()); - mainRouter.get("/rank/:id").blockingHandler(new GETRank()); - mainRouter.get("/ranks").blockingHandler(new GETRanks()); + mainRouter.get("/rank/:id").handler(new GETRank()); + mainRouter.get("/ranks").handler(new GETRanks()); mainRouter.post("/rank").blockingHandler(new POSTRank()); - //put("/rank/:id").blockingHandler(new PUTRank()); + //mainRouter.put("/rank/:id").blockingHandler(new PUTRank()); mainRouter.delete("/rank/:id").blockingHandler(new DELETERank()); - mainRouter.get("/serverGroup/:id").blockingHandler(new GETServerGroup()); - mainRouter.get("/serverGroups").blockingHandler(new GETServerGroups()); + mainRouter.get("/serverGroup/:id").handler(new GETServerGroup()); + mainRouter.get("/serverGroups").handler(new GETServerGroups()); mainRouter.post("/serverGroup").blockingHandler(new POSTServerGroup()); - //put("/serverGroup/:id").blockingHandler(new PUTServerGroup()); + //mainRouter.put("/serverGroup/:id").blockingHandler(new PUTServerGroup()); mainRouter.delete("/serverGroup/:id").blockingHandler(new DELETEServerGroup()); - mainRouter.get("/server/:id").blockingHandler(new GETServer()); - mainRouter.get("/servers").blockingHandler(new GETServers()); + mainRouter.get("/server/:id").handler(new GETServer()); + mainRouter.get("/servers").handler(new GETServers()); mainRouter.post("/server/heartbeat").handler(new POSTServerHeartbeat()); mainRouter.post("/server").blockingHandler(new POSTServer()); - //put("/server/:id").blockingHandler(new PUTServer()); + //mainRouter.put("/server/:id").blockingHandler(new PUTServer()); mainRouter.delete("/server/:id").blockingHandler(new DELETEServer()); mainRouter.get("/staff").blockingHandler(new GETStaff()); + mainRouter.get("/user/:id").blockingHandler(new GETUser()); mainRouter.get("/user/:id/details").blockingHandler(new GETUserDetails()); mainRouter.get("/user/:id/meta/:serverGroup").blockingHandler(new GETUserMeta()); - mainRouter.get("/user/:id/grants").blockingHandler(new GETUserGrants()); - mainRouter.get("/user/:id/punishments").blockingHandler(new GETUserPunishments()); - mainRouter.get("/user/:id/ipLog").blockingHandler(new GETUserIPLog()); mainRouter.get("/user/:id/requiresTOTP").blockingHandler(new GETUserRequiresTOTP()); mainRouter.get("/user/:id/verifyPassword").blockingHandler(new GETUserVerifyPassword()); - mainRouter.get("/user/:id").blockingHandler(new GETUser()); - mainRouter.post("/user/:id/verifyTOTP").blockingHandler(new POSTUserVerifyTOTP()); - mainRouter.post("/user/:id/grant").blockingHandler(new POSTUserGrant()); - mainRouter.post("/user/:id/punish").blockingHandler(new POSTUserPunish()); - mainRouter.post("/user/:id/login").blockingHandler(new POSTUserLogin()); - mainRouter.post("/user/:id/leave").blockingHandler(new POSTUserLeave()); + mainRouter.post("/user/confirmRegister/:emailToken").blockingHandler(new POSTUserConfirmRegister()); + mainRouter.post("/user/:id/leave").handler(new POSTUserLeave()); + mainRouter.post("/user/:id/login").handler(new POSTUserLogin()); mainRouter.post("/user/:id/notify").blockingHandler(new POSTUserNotify()); mainRouter.post("/user/:id/register").blockingHandler(new POSTUserRegister()); mainRouter.post("/user/:id/setupTOTP").blockingHandler(new POSTUserSetupTOTP()); - mainRouter.post("/user/confirmRegister/:emailToken").blockingHandler(new POSTUserConfirmRegister()); + mainRouter.post("/user/:id/verifyTOTP").blockingHandler(new POSTUserVerifyTOTP()); mainRouter.put("/user/:id/meta/:serverGroup").blockingHandler(new PUTUserMeta()); mainRouter.delete("/user/:id/meta/:serverGroup").blockingHandler(new DELETEUserMeta()); - mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment()); - mainRouter.getRoutes().forEach((route) -> { - 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) { - - } - }); + mainRouter.get("/dump/:type").handler(new GETDump()); + mainRouter.get("/whoami").handler(new GETWhoAmI()); + mainRouter.post("/metrics").blockingHandler(new POSTMetrics()); int port = Integer.parseInt(config.getProperty("http.port")); webServer.requestHandler(mainRouter::accept).listen(port); @@ -314,172 +309,9 @@ public final class APIv3 extends AbstractVerticle { } public static void respondJson(RoutingContext ctx, int code, Object response) { - ctx.response().putHeader("Content-Type", "application/json"); + ctx.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()); ctx.response().setStatusCode(code); ctx.response().end(gson.toJson(response)); } - private void convertData(String oldIp, boolean forReal) { - // A lot of unneeded .toString()'s and cloning objects is our ghetto null validation. - MongoDatabase importFrom = MongoClients.create(oldIp).getDatabase("minehq"); - Map mongoIdToUUID = new HashMap<>(); - AtomicInteger skippedUsers = new AtomicInteger(); - AtomicInteger skippedPunishments = new AtomicInteger(); - AtomicInteger skippedGrants = new AtomicInteger(); - AtomicInteger skippedIpLogs = new AtomicInteger(); - - SyncUtils.blockMulti(importFrom.getCollection("user").find()).forEach((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(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 (forReal) { - created.insert(); - } - - log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")"); - }); - - SyncUtils.blockMulti(importFrom.getCollection("punishment").find()).forEach((punishment) -> { - UUID target = mongoIdToUUID.get(((Map) punishment.get("user")).get("$id")); - - 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; - } - - 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"); - } - } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/Main.java b/src/main/java/net/frozenorb/apiv3/Main.java index d7b382a..d9a2f36 100644 --- a/src/main/java/net/frozenorb/apiv3/Main.java +++ b/src/main/java/net/frozenorb/apiv3/Main.java @@ -6,7 +6,6 @@ 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/ServerActor.java b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java index 07347d8..30bbdb9 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java @@ -1,6 +1,5 @@ package net.frozenorb.apiv3.actors; -import lombok.AllArgsConstructor; import lombok.Getter; import net.frozenorb.apiv3.models.Server; diff --git a/src/main/java/net/frozenorb/apiv3/actors/UserActor.java b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java index 3873270..41530ba 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/UserActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java @@ -1,13 +1,9 @@ package net.frozenorb.apiv3.actors; -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 { @Getter private final User user; diff --git a/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java b/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java index 24a33c1..77b6504 100644 --- a/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java +++ b/src/main/java/net/frozenorb/apiv3/auditLog/AuditLog.java @@ -1,8 +1,8 @@ package net.frozenorb.apiv3.auditLog; import com.google.common.collect.ImmutableMap; +import com.mongodb.async.SingleResultCallback; import lombok.experimental.UtilityClass; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.models.AuditLogEntry; import net.frozenorb.apiv3.models.User; @@ -12,14 +12,19 @@ import java.util.Map; @UtilityClass public class AuditLog { - public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType) { - log(performedBy, performedByIp, actor, actionType, ImmutableMap.of()); + public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, SingleResultCallback callback) { + log(performedBy, performedByIp, actor, actionType, ImmutableMap.of(), callback); } - public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, Map actionData) { + public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, Map actionData, SingleResultCallback callback) { AuditLogEntry entry = new AuditLogEntry(performedBy, performedByIp, actor, actionType, actionData); - entry.insert(); - APIv3.getStatsD().incrementCounter("apiv3.auditLog.insertions"); + entry.insert((ignored, error) -> { + if (error != null) { + callback.onResult(null, error); + } else { + callback.onResult(entry, null); + } + }); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/auditLog/AuditLogActionType.java b/src/main/java/net/frozenorb/apiv3/auditLog/AuditLogActionType.java index a661093..d066eb6 100644 --- a/src/main/java/net/frozenorb/apiv3/auditLog/AuditLogActionType.java +++ b/src/main/java/net/frozenorb/apiv3/auditLog/AuditLogActionType.java @@ -1,5 +1,6 @@ package net.frozenorb.apiv3.auditLog; +import com.mongodb.async.SingleResultCallback; import net.frozenorb.apiv3.models.AuditLogEntry; public enum AuditLogActionType { @@ -7,20 +8,22 @@ public enum AuditLogActionType { DELETE_PUNISHMENT { @Override - public void revert(AuditLogEntry entry) { - + public void revert(AuditLogEntry entry, SingleResultCallback callback) { + callback.onResult(false, null); } }, DELETE_GRANT { @Override - public void revert(AuditLogEntry entry) { - + public void revert(AuditLogEntry entry, SingleResultCallback callback) { + callback.onResult(false, null); } }; - public void revert(AuditLogEntry entry) {} + public void revert(AuditLogEntry entry, SingleResultCallback callback) { + callback.onResult(null, new UnsupportedOperationException()); + } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/conversion/GrantConverter.java b/src/main/java/net/frozenorb/apiv3/conversion/GrantConverter.java new file mode 100644 index 0000000..ab24c1c --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/conversion/GrantConverter.java @@ -0,0 +1,60 @@ +package net.frozenorb.apiv3.conversion; + +import com.google.common.collect.ImmutableSet; +import com.mongodb.Block; +import lombok.extern.slf4j.Slf4j; +import net.frozenorb.apiv3.models.Grant; +import org.bson.Document; +import org.bson.types.ObjectId; + +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.UUID; + +@Slf4j +public final class GrantConverter implements Block { + + private Map mongoIdToUUID; + + public GrantConverter(Map mongoIdToUUID) { + this.mongoIdToUUID = mongoIdToUUID; + } + + @Override + public void apply(Document grant) { + UUID target = mongoIdToUUID.get(((Map) grant.get("target")).get("$id")); + + if (target == null) { + 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 + ); + + created.insert(); + log.info("Created grant " + created.getId() + " (" + created.getRank() + ")"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/conversion/IpLogConverter.java b/src/main/java/net/frozenorb/apiv3/conversion/IpLogConverter.java new file mode 100644 index 0000000..ad80c21 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/conversion/IpLogConverter.java @@ -0,0 +1,57 @@ +package net.frozenorb.apiv3.conversion; + +import com.mongodb.Block; +import lombok.extern.slf4j.Slf4j; +import net.frozenorb.apiv3.models.IpLogEntry; +import net.frozenorb.apiv3.utils.IpUtils; +import org.bson.Document; +import org.bson.types.ObjectId; + +import java.util.Date; +import java.util.Map; +import java.util.UUID; + +@Slf4j +public final class IpLogConverter implements Block { + + private Map mongoIdToUUID; + + public IpLogConverter(Map mongoIdToUUID) { + this.mongoIdToUUID = mongoIdToUUID; + } + + @Override + public void apply(Document ipLogEntry) { + UUID user = mongoIdToUUID.get(((Map) ipLogEntry.get("user")).get("$id")); + + if (user == null || ipLogEntry.getString("ip") == null) { + 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() + + ); + + created.insert(); + log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/conversion/PunishmentConverter.java b/src/main/java/net/frozenorb/apiv3/conversion/PunishmentConverter.java new file mode 100644 index 0000000..317851d --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/conversion/PunishmentConverter.java @@ -0,0 +1,59 @@ +package net.frozenorb.apiv3.conversion; + +import com.google.common.collect.ImmutableMap; +import com.mongodb.Block; +import lombok.extern.slf4j.Slf4j; +import net.frozenorb.apiv3.actors.ActorType; +import net.frozenorb.apiv3.models.Punishment; +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; + +@Slf4j +public final class PunishmentConverter implements Block { + + private Map mongoIdToUUID; + + public PunishmentConverter(Map mongoIdToUUID) { + this.mongoIdToUUID = mongoIdToUUID; + } + + @Override + public void apply(Document punishment) { + UUID target = mongoIdToUUID.get(((Map) punishment.get("user")).get("$id")); + + if (target == null) { + return; + } + + // 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(), + null, + 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.containsKey("removedBy") ? (punishment.containsKey("removedAt") ? punishment.getDate("removedAt") : punishment.getDate("created")) : null, + punishment.containsKey("removedBy") ? punishment.getString("removalReason").toString() : null + ); + + created.insert(); + log.info("Created punishment " + created.getId() + " (" + created.getType() + ")"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/conversion/UserConverter.java b/src/main/java/net/frozenorb/apiv3/conversion/UserConverter.java new file mode 100644 index 0000000..ffb9223 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/conversion/UserConverter.java @@ -0,0 +1,59 @@ +package net.frozenorb.apiv3.conversion; + +import com.google.common.collect.ImmutableMap; +import com.mongodb.Block; +import lombok.extern.slf4j.Slf4j; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.utils.UUIDUtils; +import org.bson.Document; +import org.bson.types.ObjectId; + +import java.util.Map; +import java.util.UUID; + +@Slf4j +public final class UserConverter implements Block { + + private Map mongoIdToUUID; + + public UserConverter(Map mongoIdToUUID) { + this.mongoIdToUUID = mongoIdToUUID; + } + + @Override + public void apply(Document user) { + String uuidString = String.valueOf(user.get("uuid")); + + if (uuidString == null || uuidString.length() != 32 || user.get("name") == null) { + 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)) { + return; + } + + mongoIdToUUID.put(user.getObjectId("_id"), uuid); + + User created = new User( + uuid, + user.get("name").toString(), + ImmutableMap.of(user.get("name").toString(), user.getDate("joined")), + null, + null, + null, + null, + user.getString("email"), + user.getString("phone"), + "INVALID", + user.getDate("joined"), + user.getDate("joined"), + false + ); + + created.insert(); + log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/conversion/V2Converter.java b/src/main/java/net/frozenorb/apiv3/conversion/V2Converter.java new file mode 100644 index 0000000..8aaa316 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/conversion/V2Converter.java @@ -0,0 +1,69 @@ +package net.frozenorb.apiv3.conversion; + +import com.mongodb.async.SingleResultCallback; +import com.mongodb.async.client.MongoClients; +import com.mongodb.async.client.MongoDatabase; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import lombok.extern.slf4j.Slf4j; +import org.bson.types.ObjectId; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Slf4j +public final class V2Converter { + + private MongoDatabase importFrom; + + public V2Converter(String mongoIp, String database) { + importFrom = MongoClients.create(mongoIp).getDatabase(database); + } + + public void startConversion(SingleResultCallback callback) { + Map mongoIdToUUID = new HashMap<>(); + + importFrom.getCollection("user").find().forEach(new UserConverter(mongoIdToUUID), (ignored, error) -> { + if (error != null) { + callback.onResult(null, error); + return; + } + + Future punishmentsFuture = Future.future(); + Future grantsFuture = Future.future(); + Future ipLogFuture = Future.future(); + + importFrom.getCollection("punishment").find().forEach(new PunishmentConverter(mongoIdToUUID), new FutureCompatibilityCallback<>(punishmentsFuture)); + importFrom.getCollection("grant").find().forEach(new GrantConverter(mongoIdToUUID), new FutureCompatibilityCallback<>(grantsFuture)); + importFrom.getCollection("iplog").find().forEach(new IpLogConverter(mongoIdToUUID), new FutureCompatibilityCallback<>(ipLogFuture)); + + CompositeFuture.all(punishmentsFuture, grantsFuture, ipLogFuture).setHandler((result) -> { + if (result.succeeded()) { + callback.onResult(null, null); + } else { + callback.onResult(null, result.cause()); + } + }); + }); + } + + private static class FutureCompatibilityCallback implements SingleResultCallback { + + private Future future; + + public FutureCompatibilityCallback(Future future) { + this.future = future; + } + + public void onResult(T val, Throwable error) { + if (error != null) { + future.fail(error); + } else { + future.complete(val); + } + } + + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/handlers/ActorAttributeHandler.java b/src/main/java/net/frozenorb/apiv3/handlers/ActorAttributeHandler.java index 8fc1e86..0446ca9 100644 --- a/src/main/java/net/frozenorb/apiv3/handlers/ActorAttributeHandler.java +++ b/src/main/java/net/frozenorb/apiv3/handlers/ActorAttributeHandler.java @@ -26,6 +26,7 @@ public final class ActorAttributeHandler implements Handler { } } + // TODO: Cleanup private void processBasicAuthorization(String authHeader, RoutingContext ctx) { String encodedHeader = authHeader.substring("Basic ".length()); String decodedHeader = new String(Base64.getDecoder().decode(encodedHeader.getBytes())); @@ -34,24 +35,27 @@ public final class ActorAttributeHandler implements Handler { if (credentials.length == 2) { 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; - } + ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + credentials[0] + "."); + return; } - ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\""); - ErrorUtils.respondGeneric(ctx, "Failed to authorize as " + credentials[0] + "."); + String password = credentials[1]; + + if (user != null && user.getPassword() != null && user.checkPassword(password)) { + ctx.put("actor", new UserActor(user)); + ctx.next(); + return; + } else { + ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + credentials[0] + "."); + return; + } }); } else { - ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\""); - ErrorUtils.respondGeneric(ctx, "Failed to authorize as " + credentials[0] + "."); + ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + credentials[0] + "."); } } + // TODO: Cleanup private void processMHQAuthorization(String authHeader, RoutingContext ctx) { String[] split = authHeader.split(" "); @@ -71,7 +75,7 @@ public final class ActorAttributeHandler implements Handler { Server server = Server.findById(split[1]); if (server == null) { - ErrorUtils.respondNotFound(ctx, "Server", split[1]); + ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize: Server " + split[1] + " not found"); return; } @@ -95,7 +99,7 @@ public final class ActorAttributeHandler implements Handler { } } - ErrorUtils.respondGeneric(ctx, "Failed to authorize."); + ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize."); } public void processNoAuthorization(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/handlers/AuthorizationHandler.java b/src/main/java/net/frozenorb/apiv3/handlers/AuthorizationHandler.java index b25bf01..31f1a6a 100644 --- a/src/main/java/net/frozenorb/apiv3/handlers/AuthorizationHandler.java +++ b/src/main/java/net/frozenorb/apiv3/handlers/AuthorizationHandler.java @@ -2,7 +2,6 @@ package net.frozenorb.apiv3.handlers; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.utils.ErrorUtils; @@ -13,9 +12,7 @@ public final class AuthorizationHandler implements Handler { Actor actor = ctx.get("actor"); if (!actor.isAuthorized()) { - APIv3.getStatsD().incrementCounter("apiv3.http.unauthorized"); - ctx.response().putHeader("WWW-Authenticate", "Basic realm=\"MineHQ\""); - ErrorUtils.respondGeneric(ctx, "Unauthorized access: Please authorize as an approved actor. You're currently authorized as " + actor.getName()); + ErrorUtils.respondGeneric(ctx, 403, "Please authorize as an approved actor. You're currently authorized as " + actor.getName() + " (" + actor.getType() + ")"); } else { ctx.next(); } diff --git a/src/main/java/net/frozenorb/apiv3/handlers/MetricsHandler.java b/src/main/java/net/frozenorb/apiv3/handlers/MetricsHandler.java deleted file mode 100644 index 1bb0839..0000000 --- a/src/main/java/net/frozenorb/apiv3/handlers/MetricsHandler.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.frozenorb.apiv3.handlers; - -import io.vertx.core.Handler; -import io.vertx.ext.web.RoutingContext; -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(); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java index 49f7065..e7ea316 100644 --- a/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/AuditLogEntry.java @@ -3,20 +3,20 @@ package net.frozenorb.apiv3.models; import com.google.common.collect.ImmutableMap; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.ActorType; -import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; -import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.SyncUtils; import org.bson.Document; import org.bson.types.ObjectId; import java.util.*; +@Entity public final class AuditLogEntry { private static final MongoCollection auditLogCollection = APIv3.getDatabase().getCollection("auditLog", AuditLogEntry.class); @@ -31,11 +31,11 @@ public final class AuditLogEntry { @Getter private Map metadata; public static List findAllSync() { - return SyncUtils.blockMulti(auditLogCollection.find()); + return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", -1))); } public static List findAllPaginatedSync(int skip, int pageSize) { - return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", 1)).skip(skip).limit(pageSize)); + return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", -1)).skip(skip).limit(pageSize)); } public static AuditLogEntry findByIdSync(String id) { @@ -51,11 +51,11 @@ public final class AuditLogEntry { } public static void findAll(SingleResultCallback> callback) { - auditLogCollection.find().into(new ArrayList<>(), callback); + auditLogCollection.find().sort(new Document("performedAt", -1)).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); + auditLogCollection.find().sort(new Document("performedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); } public static void findById(String id, SingleResultCallback callback) { @@ -83,10 +83,8 @@ public final class AuditLogEntry { this.metadata = ImmutableMap.copyOf(metadata); } - public void insert() { - BlockingCallback callback = new BlockingCallback<>(); + public void insert(SingleResultCallback callback) { auditLogCollection.insertOne(this, callback); - callback.get(); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/Grant.java b/src/main/java/net/frozenorb/apiv3/models/Grant.java index cda879e..86d30d0 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Grant.java +++ b/src/main/java/net/frozenorb/apiv3/models/Grant.java @@ -3,7 +3,8 @@ 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 fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; @@ -14,7 +15,9 @@ import org.bson.Document; import org.bson.types.ObjectId; import java.util.*; +import java.util.stream.Collectors; +@Entity @AllArgsConstructor public final class Grant { @@ -35,15 +38,16 @@ public final class Grant { @Getter private String removalReason; public static List findAllSync() { - return SyncUtils.blockMulti(grantsCollection.find()); + return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", -1))); } public static List findAllPaginatedSync(int skip, int pageSize) { - return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(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 List findByRankSync(Collection ranks) { + Collection convertedRanks = ranks.stream().map(Rank::getId).collect(Collectors.toList()); + return SyncUtils.blockMulti(grantsCollection.find(new Document("rank", new Document("$in", convertedRanks)))); } public static Grant findByIdSync(String id) { @@ -59,15 +63,16 @@ public final class Grant { } public static void findAll(SingleResultCallback> callback) { - grantsCollection.find().into(new ArrayList<>(), callback); + grantsCollection.find().sort(new Document("addedAt", -1)).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); + 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 findByRank(Collection ranks, SingleResultCallback> callback) { + Collection convertedRanks = ranks.stream().map(Rank::getId).collect(Collectors.toList()); + grantsCollection.find(new Document("rank", new Document("$in", convertedRanks))).into(new ArrayList<>(), callback); } public static void findById(String id, SingleResultCallback callback) { @@ -150,8 +155,8 @@ public final class Grant { this.removedAt = new Date(); this.removalReason = reason; - BlockingCallback callback = new BlockingCallback<>(); - grantsCollection.deleteOne(new Document("_id", id), callback); + BlockingCallback callback = new BlockingCallback<>(); + grantsCollection.replaceOne(new Document("_id", id), this, callback); callback.get(); } diff --git a/src/main/java/net/frozenorb/apiv3/models/IPBan.java b/src/main/java/net/frozenorb/apiv3/models/IPBan.java new file mode 100644 index 0000000..6dc3fe4 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/models/IPBan.java @@ -0,0 +1,171 @@ +package net.frozenorb.apiv3.models; + +import com.mongodb.async.SingleResultCallback; +import com.mongodb.async.client.MongoCollection; +import com.mongodb.client.result.UpdateResult; +import fr.javatic.mongo.jacksonCodec.Entity; +import fr.javatic.mongo.jacksonCodec.objectId.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.actors.Actor; +import net.frozenorb.apiv3.actors.ActorType; +import net.frozenorb.apiv3.unsorted.BlockingCallback; +import net.frozenorb.apiv3.utils.SyncUtils; +import net.frozenorb.apiv3.utils.TimeUtils; +import org.bson.Document; +import org.bson.types.ObjectId; + +import java.util.*; + +@Entity +@AllArgsConstructor +public final class IpBan { + + private static final MongoCollection ipBansCollection = APIv3.getDatabase().getCollection("ipBans", IpBan.class); + + @Getter @Id private String id; + @Getter private String userIp; + @Getter private String reason; + @Getter private Date expiresAt; + + @Getter private UUID addedBy; + @Getter private Date addedAt; + @Getter private String actorName; + @Getter private ActorType actorType; + + @Getter private UUID removedBy; + @Getter private Date removedAt; + @Getter private String removalReason; + + public static List findAllSync() { + return SyncUtils.blockMulti(ipBansCollection.find().sort(new Document("addedAt", -1))); + } + + public static List findAllPaginatedSync(int skip, int pageSize) { + return SyncUtils.blockMulti(ipBansCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize)); + } + + public static IpBan findByIdSync(String id) { + return SyncUtils.blockOne(ipBansCollection.find(new Document("_id", id))); + } + + public static List findByIpSync(String userIp) { + return SyncUtils.blockMulti(ipBansCollection.find(new Document("userIp", userIp))); + } + + public static void findAll(SingleResultCallback> callback) { + ipBansCollection.find().sort(new Document("addedAt", -1)).into(new ArrayList<>(), callback); + } + + public static void findAllPaginated(int skip, int pageSize, SingleResultCallback> callback) { + ipBansCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); + } + + public static void findById(String id, SingleResultCallback callback) { + ipBansCollection.find(new Document("_id", id)).first(callback); + } + + public static void findByIp(String userIp, SingleResultCallback> callback) { + ipBansCollection.find(new Document("userIp", userIp)).into(new ArrayList<>(), callback); + } + + public static void findByIpGrouped(Iterable userIps, SingleResultCallback>> callback) { + ipBansCollection.find(new Document("userIp", new Document("$in", userIps))).into(new ArrayList<>(), (ipBans, error) -> { + if (error != null) { + callback.onResult(null, error); + } else { + Map> result = new HashMap<>(); + + for (String userIp : userIps) { + result.put(userIp, new ArrayList<>()); + } + + for (IpBan ipBan : ipBans) { + result.get(ipBan.getUserIp()).add(ipBan); + } + + callback.onResult(result, null); + } + }); + } + + public IpBan() {} // For Morphia + + public IpBan(String userIp, Punishment linked) { + this.id = new ObjectId().toString(); + this.userIp = userIp; + this.reason = linked.getReason(); + this.expiresAt = linked.getExpiresAt(); + this.addedBy = linked.getAddedBy(); + this.addedAt = new Date(); + this.actorName = linked.getActorName(); + this.actorType = linked.getActorType(); + } + + public IpBan(String userIp, String reason, Date expiresAt, User addedBy, Actor actor) { + this.id = new ObjectId().toString(); + this.userIp = userIp; + this.reason = reason; + this.expiresAt = expiresAt; + this.addedBy = addedBy == null ? null : addedBy.getId(); + this.addedAt = new Date(); + this.actorName = actor.getName(); + this.actorType = actor.getType(); + } + + public boolean isActive() { + return !(isExpired() || isRemoved()); + } + + public boolean isExpired() { + if (expiresAt == null) { + return false; // Never expires + } else { + return expiresAt.before(new Date()); + } + } + + public boolean isRemoved() { + return removedBy != null; + } + + public String getAccessDenialReason() { + String accessDenialReason = "Your ip address has been suspended from the MineHQ Network. \n\n"; + + if (getExpiresAt() != null) { + accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), new Date())); + } else { + accessDenialReason += "Appeal at MineHQ.com/appeal"; + } + + return accessDenialReason; + } + + public void insert() { + BlockingCallback callback = new BlockingCallback<>(); + ipBansCollection.insertOne(this, callback); + callback.get(); + } + + public void delete(User removedBy, String reason) { + this.removedBy = removedBy.getId(); + this.removedAt = new Date(); + this.removalReason = reason; + + BlockingCallback callback = new BlockingCallback<>(); + ipBansCollection.replaceOne(new Document("_id", id), this, callback); + callback.get(); + } + + public void delete(Punishment linkedPunishment) { + this.removedBy = linkedPunishment.getRemovedBy(); + this.removedAt = new Date(); + this.removalReason = "Linked punishment removed: " + linkedPunishment.getRemovalReason(); + + BlockingCallback callback = new BlockingCallback<>(); + ipBansCollection.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/IPLogEntry.java b/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java index 98f20d7..7e9bfd0 100644 --- a/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/IPLogEntry.java @@ -3,6 +3,7 @@ package net.frozenorb.apiv3.models; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.UpdateResult; +import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; @@ -17,10 +18,11 @@ import java.util.Date; import java.util.List; import java.util.UUID; +@Entity @AllArgsConstructor -public final class IPLogEntry { +public final class IpLogEntry { - private static final MongoCollection ipLogCollection = APIv3.getDatabase().getCollection("ipLog", IPLogEntry.class); + private static final MongoCollection ipLogCollection = APIv3.getDatabase().getCollection("ipLog", IpLogEntry.class); @Getter @Id private String id; @Getter private UUID user; @@ -29,57 +31,57 @@ public final class IPLogEntry { @Getter private Date lastSeenAt; @Getter private int uses; - public static List findAllSync() { - return SyncUtils.blockMulti(ipLogCollection.find()); + public static List findAllSync() { + return SyncUtils.blockMulti(ipLogCollection.find().sort(new Document("lastSeenAt", -1))); } - public static IPLogEntry findByIdSync(String id) { + public static IpLogEntry findByIdSync(String id) { return SyncUtils.blockOne(ipLogCollection.find(new Document("_id", id))); } - public static List findByUserSync(User user) { + public static List findByUserSync(User user) { return findByUserSync(user.getId()); } - public static List findByUserSync(UUID user) { - return SyncUtils.blockMulti(ipLogCollection.find(new Document("user", user))); + public static List findByUserSync(UUID user) { + return SyncUtils.blockMulti(ipLogCollection.find(new Document("user", user)).sort(new Document("lastSeenAt", -1))); } - public static IPLogEntry findByUserAndIpSync(User user, String userIp) { + public static IpLogEntry findByUserAndIpSync(User user, String userIp) { return findByUserAndIpSync(user.getId(), userIp); } - public static IPLogEntry findByUserAndIpSync(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 findAll(SingleResultCallback> callback) { + ipLogCollection.find().sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback); } - public static void findById(String id, SingleResultCallback 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) { + 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 findByUser(UUID user, SingleResultCallback> callback) { + ipLogCollection.find(new Document("user", user)).sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback); } - public static void findByUserAndIp(User user, String userIp, SingleResultCallback 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) { + 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() {} // For Morphia - public IPLogEntry(User user, String userIp) { + public IpLogEntry(User user, String userIp) { this.id = new ObjectId().toString(); this.user = user.getId(); this.userIp = userIp; diff --git a/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java index 8d000c5..f1fffa2 100644 --- a/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/models/NotificationTemplate.java @@ -4,12 +4,12 @@ import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; +import fr.javatic.mongo.jacksonCodec.Entity; 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; @@ -17,6 +17,7 @@ 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); diff --git a/src/main/java/net/frozenorb/apiv3/models/Punishment.java b/src/main/java/net/frozenorb/apiv3/models/Punishment.java index 3a42564..ce093e8 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Punishment.java +++ b/src/main/java/net/frozenorb/apiv3/models/Punishment.java @@ -2,7 +2,8 @@ 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 fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; @@ -16,7 +17,9 @@ import org.bson.Document; import org.bson.types.ObjectId; import java.util.*; +import java.util.stream.Collectors; +@Entity @AllArgsConstructor public final class Punishment { @@ -28,6 +31,7 @@ public final class Punishment { @Getter private PunishmentType type; @Getter private Date expiresAt; @Getter private Map metadata; + @Getter private String linkedIpBanId; @Getter private UUID addedBy; @Getter private Date addedAt; @@ -39,15 +43,16 @@ public final class Punishment { @Getter private String removalReason; public static List findAllSync() { - return SyncUtils.blockMulti(punishmentsCollection.find()); + return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", -1))); } public static List findAllPaginatedSync(int skip, int pageSize) { - return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", 1)).skip(skip).limit(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 List findByTypeSync(Collection types) { + Collection convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList()); + return SyncUtils.blockMulti(punishmentsCollection.find(new Document("type", new Document("$in", convertedTypes)))); } public static Punishment findByIdSync(String id) { @@ -62,24 +67,26 @@ public final class Punishment { return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user))); } - public static List findByUserAndTypeSync(User user, Iterable types) { + public static List findByUserAndTypeSync(User user, Collection types) { return findByUserAndTypeSync(user.getId(), 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 List findByUserAndTypeSync(UUID user, Collection types) { + Collection convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList()); + return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes)))); } public static void findAll(SingleResultCallback> callback) { - punishmentsCollection.find().into(new ArrayList<>(), callback); + punishmentsCollection.find().sort(new Document("addedAt", -1)).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); + 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 findByType(Collection types, SingleResultCallback> callback) { + Collection convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList()); + punishmentsCollection.find(new Document("type", new Document("$in", convertedTypes))).into(new ArrayList<>(), callback); } public static void findById(String id, SingleResultCallback callback) { @@ -114,12 +121,13 @@ public final class Punishment { }); } - public static void findByUserAndType(User user, Iterable types, SingleResultCallback> callback) { + public static void findByUserAndType(User user, Collection 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 static void findByUserAndType(UUID user, Collection types, SingleResultCallback> callback) { + Collection convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList()); + punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes))).into(new ArrayList<>(), callback); } public Punishment() {} // For Morphia @@ -172,6 +180,10 @@ public final class Punishment { } } + public void linkIpBan(IpBan ipBan) { + + } + public void insert() { BlockingCallback callback = new BlockingCallback<>(); punishmentsCollection.insertOne(this, callback); @@ -183,8 +195,8 @@ public final class Punishment { this.removedAt = new Date(); this.removalReason = reason; - BlockingCallback callback = new BlockingCallback<>(); - punishmentsCollection.deleteOne(new Document("_id", id), callback); + BlockingCallback callback = new BlockingCallback<>(); + punishmentsCollection.replaceOne(new Document("_id", id), this, callback); callback.get(); } diff --git a/src/main/java/net/frozenorb/apiv3/models/Rank.java b/src/main/java/net/frozenorb/apiv3/models/Rank.java index 8ec7348..5af8936 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Rank.java +++ b/src/main/java/net/frozenorb/apiv3/models/Rank.java @@ -5,6 +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 fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import net.frozenorb.apiv3.APIv3; @@ -18,6 +19,7 @@ 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 d666da9..8898cce 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Server.java +++ b/src/main/java/net/frozenorb/apiv3/models/Server.java @@ -4,11 +4,12 @@ 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 fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.serialization.ExcludeFromReplies; +import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies; import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.SyncUtils; import org.bson.Document; @@ -16,6 +17,7 @@ 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 5d41865..92a1d29 100644 --- a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java @@ -1,14 +1,14 @@ package net.frozenorb.apiv3.models; -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 fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.serialization.ExcludeFromReplies; +import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies; import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.PermissionUtils; import net.frozenorb.apiv3.utils.SyncUtils; @@ -17,6 +17,7 @@ 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); @@ -32,6 +33,7 @@ public final class ServerGroup { @Getter @Setter @ExcludeFromReplies private Set announcements = new HashSet<>(); @Getter @Setter @ExcludeFromReplies private Map> permissions = new HashMap<>(); + // make this and other stuff async public static List findAll() { updateCacheIfNeeded(); return serverGroupAltCache; diff --git a/src/main/java/net/frozenorb/apiv3/models/User.java b/src/main/java/net/frozenorb/apiv3/models/User.java index 3b6ccb7..ba43809 100644 --- a/src/main/java/net/frozenorb/apiv3/models/User.java +++ b/src/main/java/net/frozenorb/apiv3/models/User.java @@ -1,5 +1,6 @@ package net.frozenorb.apiv3.models; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.base.Charsets; @@ -9,14 +10,17 @@ import com.google.common.hash.Hashing; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.UpdateResult; +import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; 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.serialization.gson.ExcludeFromReplies; +import net.frozenorb.apiv3.serialization.jackson.UUIDJsonDeserializer; +import net.frozenorb.apiv3.serialization.jackson.UUIDJsonSerializer; import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.MojangUtils; import net.frozenorb.apiv3.utils.PermissionUtils; @@ -24,8 +28,10 @@ import net.frozenorb.apiv3.utils.SyncUtils; import net.frozenorb.apiv3.utils.UUIDUtils; import org.bson.Document; +import java.io.IOException; import java.util.*; +@Entity @AllArgsConstructor public final class User { @@ -46,7 +52,7 @@ public final class User { @Getter private boolean online; public static List findAllSync() { - return SyncUtils.blockMulti(usersCollection.find()); + return SyncUtils.blockMulti(usersCollection.find().sort(new Document("lastSeenAt", -1))); } public static User findByIdSync(String id) { @@ -78,7 +84,7 @@ public final class User { } public static void findAll(SingleResultCallback> callback) { - usersCollection.find().into(new ArrayList<>(), callback); + usersCollection.find().sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback); } public static void findById(String id, SingleResultCallback callback) { @@ -140,7 +146,10 @@ public final class User { this.lastSeenAt = new Date(); this.firstSeenAt = new Date(); - updateUsername(lastUsername); + // TODO: MAKE THIS ASYNC? SOMEHOW? + BlockingCallback blockingCallback = new BlockingCallback<>(); + updateUsername(lastUsername, blockingCallback); + blockingCallback.get(); } public boolean hasPermissionAnywhere(String permission) { @@ -180,21 +189,31 @@ public final class User { this.online = false; } - public void updateUsername(String username) { - if (!username.equals(lastUsername)) { - this.lastUsername = username; + public void updateUsername(String newUsername, SingleResultCallback callback) { + this.aliases.put(newUsername, new Date()); - User withNewUsername; - - while ((withNewUsername = User.findByLastUsernameSync(username)) != null) { - BlockingCallback callback = new BlockingCallback<>(); - MojangUtils.getName(withNewUsername.getId(), callback); - String newUsername = callback.get(); - withNewUsername.updateUsername(newUsername); - } + if (newUsername.equalsIgnoreCase(lastUsername)) { + callback.onResult(null, null); + return; } - this.aliases.put(username, new Date()); + this.lastUsername = newUsername; + + User.findByLastUsername(newUsername, (otherUser, error) -> { + if (error != null) { + callback.onResult(null, error); + } else if (otherUser != null) { + MojangUtils.getName(otherUser.getId(), (newName, error2) -> { + if (error2 != null) { + callback.onResult(null, error2); + } else { + otherUser.updateUsername(newName, callback); + } + }); + } else { + callback.onResult(null, null); + } + }); } public void setPassword(String input) { @@ -272,22 +291,46 @@ public final class User { return highestRanks; } - public Map getLoginInfo(Server server) { - return createLoginInfo( - server, - Punishment.findByUserAndTypeSync(this, ImmutableSet.of( - Punishment.PunishmentType.BLACKLIST, - Punishment.PunishmentType.BAN, - Punishment.PunishmentType.MUTE - )), - Grant.findByUserSync(this) - ); + public void getLoginInfo(Server server, SingleResultCallback> callback) { + Future> punishmentsFuture = Future.future(); + Future> grantsFuture = Future.future(); + + Punishment.findByUserAndType(this, ImmutableSet.of( + Punishment.PunishmentType.BLACKLIST, + Punishment.PunishmentType.BAN, + Punishment.PunishmentType.MUTE + ), (punishments, error) -> { + if (error != null) { + punishmentsFuture.fail(error); + } else { + punishmentsFuture.complete(punishments); + } + }); + + Grant.findByUser(this, (grants, error) -> { + if (error != null) { + grantsFuture.fail(error); + } else { + grantsFuture.complete(grants); + } + }); + + CompositeFuture.all(punishmentsFuture, grantsFuture).setHandler((result) -> { + if (result.succeeded()) { + Iterable punishments = result.result().result(0); + Iterable grants = result.result().result(1); + + callback.onResult(createLoginInfo(server, punishments, grants), null); + } else { + callback.onResult(null, result.cause()); + } + }); } // This is only used to help batch requests to mongo public Map createLoginInfo(Server server, Iterable punishments, Iterable grants) { Punishment activeMute = null; - String accessDenialReason = null; + Punishment activeBan = null; for (Punishment punishment : punishments) { if (!punishment.isActive()) { @@ -296,8 +339,8 @@ public final class User { if (punishment.getType() == Punishment.PunishmentType.MUTE) { activeMute = punishment; - } else { - accessDenialReason = punishment.getAccessDenialReason(); + } else if (punishment.getType() == Punishment.PunishmentType.BAN || punishment.getType() == Punishment.PunishmentType.BLACKLIST) { + activeBan = punishment; } } @@ -307,8 +350,9 @@ public final class User { ImmutableMap.Builder result = ImmutableMap.builder() .put("user", this) .put("access", ImmutableMap.of( - "allowed", accessDenialReason == null, - "message", accessDenialReason == null ? "Public server" : accessDenialReason + "allowed", activeBan == null, + "message", activeBan == null ? "Public server" : activeBan.getAccessDenialReason(), + "activeBanId", activeBan == null ? "" : activeBan.getId() )) .put("rank", highestRank.getId()) .put("totpSetup", getTotpSecret() != null); @@ -328,8 +372,12 @@ public final class User { public void save() { BlockingCallback callback = new BlockingCallback<>(); - usersCollection.replaceOne(new Document("_id", id), this, callback); + save(callback); callback.get(); } + public void save(SingleResultCallback callback) { + usersCollection.replaceOne(new Document("_id", id), this, callback); + } + } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java index 5f1831d..839d1d2 100644 --- a/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java +++ b/src/main/java/net/frozenorb/apiv3/models/UserMetaEntry.java @@ -5,6 +5,7 @@ import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; +import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.Getter; import lombok.Setter; @@ -19,6 +20,7 @@ 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); diff --git a/src/main/java/net/frozenorb/apiv3/routes/GETDump.java b/src/main/java/net/frozenorb/apiv3/routes/GETDump.java index 27629a1..b5d7f70 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/GETDump.java +++ b/src/main/java/net/frozenorb/apiv3/routes/GETDump.java @@ -18,6 +18,7 @@ public final class GETDump implements Handler { private Map> grantCache = new HashMap<>(); public GETDump() { + // TODO: Use vertx scheduler Thread dumpUpdater = new Thread() { @Override @@ -69,7 +70,7 @@ public final class GETDump implements Handler { try { Thread.sleep(TimeUnit.MINUTES.toMillis(3)); - } catch (Exception ex) { + } catch (InterruptedException ex) { throw new RuntimeException(ex); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java b/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java index dd6b553..23d19ba 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java +++ b/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java @@ -3,27 +3,20 @@ package net.frozenorb.apiv3.routes.announcements; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.actors.Actor; -import net.frozenorb.apiv3.actors.ActorType; -import net.frozenorb.apiv3.actors.ServerActor; -import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.utils.ErrorUtils; public final class GETAnnouncements implements Handler { public void handle(RoutingContext ctx) { - Actor actor = ctx.get("actor"); + ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("id")); - if (actor.getType() != ActorType.SERVER) { - ErrorUtils.respondServerOnly(ctx); + if (serverGroup == null) { + ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id")); return; } - Server sender = ((ServerActor) actor).getServer(); - ServerGroup senderGroup = ServerGroup.findById(sender.getServerGroup()); - - APIv3.respondJson(ctx, senderGroup.getAnnouncements()); + APIv3.respondJson(ctx, serverGroup.getAnnouncements()); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/announcements/PUTAnnouncements.java b/src/main/java/net/frozenorb/apiv3/routes/announcements/PUTAnnouncements.java new file mode 100644 index 0000000..d42dd2f --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/announcements/PUTAnnouncements.java @@ -0,0 +1,33 @@ +package net.frozenorb.apiv3.routes.announcements; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.models.ServerGroup; +import net.frozenorb.apiv3.utils.ErrorUtils; + +import java.util.HashSet; +import java.util.Set; + +public final class PUTAnnouncements implements Handler { + + public void handle(RoutingContext ctx) { + ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("id")); + + if (serverGroup == null) { + ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id")); + return; + } + + Set announcements = new HashSet<>(); + + for (Object announcement : ctx.getBodyAsJsonArray()) { + announcements.add((String) announcement); + } + + serverGroup.setAnnouncements(announcements); + serverGroup.save(); + APIv3.respondJson(ctx, serverGroup.getAnnouncements()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/auditLog/POSTUserAuditLogEntry.java b/src/main/java/net/frozenorb/apiv3/routes/auditLog/POSTUserAuditLogEntry.java new file mode 100644 index 0000000..336ea66 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/auditLog/POSTUserAuditLogEntry.java @@ -0,0 +1,47 @@ +package net.frozenorb.apiv3.routes.auditLog; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.auditLog.AuditLog; +import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.models.AuditLogEntry; +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 org.bson.Document; + +public final class POSTUserAuditLogEntry implements Handler { + + public void handle(RoutingContext ctx) { + User user = User.findByIdSync(ctx.request().getParam("id")); + + if (user == null) { + ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); + return; + } + + String userIp = ctx.request().getParam("userIp"); + + if (!IpUtils.isValidIp(userIp)) { + ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); + return; + } + + AuditLogActionType type; + + try { + type = AuditLogActionType.valueOf(ctx.request().getParam("type")); + } catch (IllegalArgumentException ex) { + ErrorUtils.respondNotFound(ctx, "Audit log action type", ctx.request().getParam("type")); + return; + } + + BlockingCallback blockingCallback = new BlockingCallback<>(); + AuditLog.log(user, userIp, ctx.get("actor"), type, Document.parse(ctx.getBodyAsString()), blockingCallback); + AuditLogEntry entry = blockingCallback.get(); + APIv3.respondJson(ctx, entry); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java b/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java index 879edbf..972b000 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java +++ b/src/main/java/net/frozenorb/apiv3/routes/chatFilterList/GETChatFilterList.java @@ -1,6 +1,6 @@ package net.frozenorb.apiv3.routes.chatFilterList; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; @@ -8,7 +8,9 @@ import net.frozenorb.apiv3.APIv3; public final class GETChatFilterList implements Handler { public void handle(RoutingContext ctx) { - APIv3.respondJson(ctx, ImmutableMap.of()); + // TODO + // WARNING: THIS IS REGISTERED ASYNC SO DONT MESS IT UP + APIv3.respondJson(ctx, ImmutableList.of()); } } \ No newline at end of file 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 d96e6bb..9eeb99a 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java @@ -6,8 +6,10 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.models.AuditLogEntry; import net.frozenorb.apiv3.models.Grant; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.ErrorUtils; public final class DELETEGrant implements Handler { @@ -38,7 +40,9 @@ public final class DELETEGrant implements Handler { } grant.delete(removedBy, reason); - AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of()); + BlockingCallback blockingCallback = new BlockingCallback<>(); + AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of(), blockingCallback); + blockingCallback.get(); APIv3.respondJson(ctx, grant); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipBans/DELETEIpBan.java b/src/main/java/net/frozenorb/apiv3/routes/ipBans/DELETEIpBan.java new file mode 100644 index 0000000..7682520 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/ipBans/DELETEIpBan.java @@ -0,0 +1,49 @@ +package net.frozenorb.apiv3.routes.ipBans; + +import com.google.common.collect.ImmutableMap; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.auditLog.AuditLog; +import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.models.AuditLogEntry; +import net.frozenorb.apiv3.models.IpBan; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; +import net.frozenorb.apiv3.utils.ErrorUtils; + +public final class DELETEIpBan implements Handler { + + public void handle(RoutingContext ctx) { + IpBan ipBan = IpBan.findByIdSync(ctx.request().getParam("id")); + + if (ipBan == null) { + ErrorUtils.respondNotFound(ctx, "IpBan", ctx.request().getParam("id")); + return; + } else if (!ipBan.isActive()) { + ErrorUtils.respondInvalidInput(ctx, "Cannot remove an inactive ip ban."); + return; + } + + User removedBy = User.findByIdSync(ctx.request().getParam("removedBy")); + + if (removedBy == null) { + ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); + return; + } + + String reason = ctx.request().getParam("reason"); + + if (reason == null || reason.trim().isEmpty()) { + ErrorUtils.respondRequiredInput(ctx, "reason"); + return; + } + + ipBan.delete(removedBy, reason); + BlockingCallback blockingCallback = new BlockingCallback<>(); + AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback); + blockingCallback.get(); + APIv3.respondJson(ctx, ipBan); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpBan.java b/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpBan.java new file mode 100644 index 0000000..268b46d --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpBan.java @@ -0,0 +1,14 @@ +package net.frozenorb.apiv3.routes.ipBans; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.models.IpBan; + +public final class GETIpBan implements Handler { + + public void handle(RoutingContext ctx) { + APIv3.respondJson(ctx, IpBan.findByIdSync(ctx.request().getParam("id"))); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpBans.java b/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpBans.java new file mode 100644 index 0000000..36cf2d3 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpBans.java @@ -0,0 +1,22 @@ +package net.frozenorb.apiv3.routes.ipBans; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.models.IpBan; +import net.frozenorb.apiv3.utils.ErrorUtils; + +public final class GETIpBans implements Handler { + + public void handle(RoutingContext ctx) { + try { + 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, IpBan.findAllPaginatedSync(skip, pageSize)); + } catch (NumberFormatException ex) { + ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpIpBans.java b/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpIpBans.java new file mode 100644 index 0000000..f798a00 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/ipBans/GETIpIpBans.java @@ -0,0 +1,24 @@ +package net.frozenorb.apiv3.routes.ipBans; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.models.IpBan; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.utils.ErrorUtils; +import net.frozenorb.apiv3.utils.IpUtils; + +public final class GETIpIpBans implements Handler { + + public void handle(RoutingContext ctx) { + String userIp = ctx.request().getParam("id"); + + if (!IpUtils.isValidIp(userIp)) { + ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); + return; + } + + APIv3.respondJson(ctx, IpBan.findByIpSync(userIp)); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/ipBans/POSTIpIpBan.java b/src/main/java/net/frozenorb/apiv3/routes/ipBans/POSTIpIpBan.java new file mode 100644 index 0000000..b478731 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/ipBans/POSTIpIpBan.java @@ -0,0 +1,57 @@ +package net.frozenorb.apiv3.routes.ipBans; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.models.IpBan; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.Permissions; +import net.frozenorb.apiv3.utils.ErrorUtils; +import net.frozenorb.apiv3.utils.IpUtils; +import org.bson.Document; + +import java.util.Date; +import java.util.Map; + +public final class POSTIpIpBan implements Handler { + + public void handle(RoutingContext ctx) { + String userIp = ctx.request().getParam("id"); + + if (!IpUtils.isValidIp(userIp)) { + ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); + return; + } + + String reason = ctx.request().getParam("reason"); + + if (reason == null || reason.trim().isEmpty()) { + ErrorUtils.respondRequiredInput(ctx, "reason"); + return; + } + + Date expiresAt; + + try { + expiresAt = new Date(Long.parseLong(ctx.request().getParam("expiresAt"))); + } catch (NumberFormatException ex) { + expiresAt = null; + } + + if (expiresAt != null && expiresAt.before(new Date())) { + ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past."); + return; + } + + // We purposely don't do a null check, ip bans don't have to have a source. + User addedBy = User.findByIdSync(ctx.request().getParam("addedBy")); + + IpBan ipBan = new IpBan(userIp, reason, expiresAt, addedBy, ctx.get("actor")); + ipBan.insert(); + + APIv3.respondJson(ctx, ipBan); + } + +} \ No newline at end of file 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 42e5310..a36f49a 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java +++ b/src/main/java/net/frozenorb/apiv3/routes/ipLog/GETUserIPLog.java @@ -3,11 +3,11 @@ package net.frozenorb.apiv3.routes.ipLog; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.models.IPLogEntry; +import net.frozenorb.apiv3.models.IpLogEntry; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.utils.ErrorUtils; -public final class GETUserIPLog implements Handler { +public final class GETUserIpLog implements Handler { public void handle(RoutingContext ctx) { User target = User.findByIdSync(ctx.request().getParam("id")); @@ -17,7 +17,7 @@ public final class GETUserIPLog implements Handler { return; } - APIv3.respondJson(ctx, IPLogEntry.findByUserSync(target)); + APIv3.respondJson(ctx, IpLogEntry.findByUserSync(target)); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/PUTNotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/PUTNotificationTemplate.java deleted file mode 100644 index 03c9431..0000000 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/PUTNotificationTemplate.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.frozenorb.apiv3.routes.notificationTemplate; - -public class PUTNotificationTemplate { -} diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/DELETENotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/DELETENotificationTemplate.java similarity index 92% rename from src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/DELETENotificationTemplate.java rename to src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/DELETENotificationTemplate.java index da8850f..59e7d58 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/DELETENotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/DELETENotificationTemplate.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.routes.notificationTemplate; +package net.frozenorb.apiv3.routes.notificationTemplates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/GETNotificationTemplate.java similarity index 86% rename from src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplate.java rename to src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/GETNotificationTemplate.java index 8e6632f..d7425eb 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/GETNotificationTemplate.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.routes.notificationTemplate; +package net.frozenorb.apiv3.routes.notificationTemplates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplates.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/GETNotificationTemplates.java similarity index 86% rename from src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplates.java rename to src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/GETNotificationTemplates.java index 16cdba6..f91ee86 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/GETNotificationTemplates.java +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/GETNotificationTemplates.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.routes.notificationTemplate; +package net.frozenorb.apiv3.routes.notificationTemplates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/POSTNotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/POSTNotificationTemplate.java similarity index 91% rename from src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/POSTNotificationTemplate.java rename to src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/POSTNotificationTemplate.java index d913b96..09ee2e9 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplate/POSTNotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/POSTNotificationTemplate.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.routes.notificationTemplate; +package net.frozenorb.apiv3.routes.notificationTemplates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; diff --git a/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/PUTNotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/PUTNotificationTemplate.java new file mode 100644 index 0000000..c07f9a9 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/notificationTemplates/PUTNotificationTemplate.java @@ -0,0 +1,4 @@ +package net.frozenorb.apiv3.routes.notificationTemplates; + +public class PUTNotificationTemplate { +} 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 33e07bc..d545ed3 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java @@ -6,8 +6,10 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.models.AuditLogEntry; import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.ErrorUtils; public final class DELETEPunishment implements Handler { @@ -38,7 +40,9 @@ public final class DELETEPunishment implements Handler { } punishment.delete(removedBy, reason); - AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of()); + BlockingCallback blockingCallback = new BlockingCallback<>(); + AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback); + blockingCallback.get(); APIv3.respondJson(ctx, punishment); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEUserPunishment.java similarity index 79% rename from src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java rename to src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEUserPunishment.java index b2f8a50..76e9828 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEUserPunishment.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.routes.users; +package net.frozenorb.apiv3.routes.punishments; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -7,8 +7,10 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.models.AuditLogEntry; import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.utils.ErrorUtils; public final class DELETEUserPunishment implements Handler { @@ -39,13 +41,15 @@ public final class DELETEUserPunishment implements Handler { 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()); + BlockingCallback blockingCallback = new BlockingCallback<>(); + AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback); + blockingCallback.get(); APIv3.respondJson(ctx, punishment); return; } } - ErrorUtils.respondGeneric(ctx, "User provided has no active punishments"); + ErrorUtils.respondGeneric(ctx, 404, "User provided has no active punishments"); } } \ 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 bd2ef45..5c8433c 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java @@ -36,7 +36,7 @@ public final class POSTUserPunish implements Handler { if (type != Punishment.PunishmentType.WARN) { for (Punishment punishment : Punishment.findByUserAndTypeSync(target, ImmutableSet.of(type))) { if (punishment.isActive()) { - ErrorUtils.respondGeneric(ctx, "A punishment by " + User.findByIdSync(punishment.getAddedBy()).getLastUsername() + " already covers this user."); + ErrorUtils.respondGeneric(ctx, 200, "A punishment by " + User.findByIdSync(punishment.getAddedBy()).getLastUsername() + " already covers this user."); return; } } @@ -62,11 +62,11 @@ public final class POSTUserPunish implements Handler { return; } - // We purposely don't do a null check, grants don't have to have a source. + // We purposely don't do a null check, punishments don't have to have a source. User addedBy = User.findByIdSync(ctx.request().getParam("addedBy")); if (target.hasPermissionAnywhere(Permissions.PROTECTED_PUNISHMENT)) { - ErrorUtils.respondGeneric(ctx, target.getLastSeenOn() + " is protected from punishments."); + ErrorUtils.respondGeneric(ctx, 200, target.getLastSeenOn() + " is protected from punishments."); return; } diff --git a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java index 69e2f5f..03a079d 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java +++ b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java @@ -6,14 +6,16 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.utils.ErrorUtils; -import net.frozenorb.apiv3.utils.IPUtils; +import net.frozenorb.apiv3.utils.IpUtils; + +import java.math.BigInteger; +import java.util.Random; public final class POSTServer implements Handler { public void handle(RoutingContext ctx) { String id = ctx.request().getParam("id"); String displayName = ctx.request().getParam("displayName"); - String apiKey = ctx.request().getParam("apiKey"); ServerGroup group = ServerGroup.findById(ctx.request().getParam("group")); String ip = ctx.request().getParam("ip"); @@ -22,12 +24,13 @@ public final class POSTServer implements Handler { return; } - if (!IPUtils.isValidIP(ip)) { - ErrorUtils.respondInvalidInput(ctx, "IP address \"" + ip + "\" is not valid."); + if (!IpUtils.isValidIp(ip)) { + ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + ip + "\" is not valid."); return; } - Server server = new Server(id, displayName, apiKey, group, ip); + String generatedApiKey = new BigInteger(130, new Random()).toString(32); + Server server = new Server(id, displayName, generatedApiKey, group, ip); server.insert(); APIv3.respondJson(ctx, server); } 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 341accd..00414a1 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,5 @@ 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; @@ -35,32 +34,35 @@ public final class POSTServerHeartbeat implements Handler { Map playerNames = extractPlayerNames(reqJson); CompositeFuture.all( - createInfoResponse(actorServer, reqJson.getDouble("tps"), playerNames), + createInfoResponse(actorServer, reqJson.getDouble("lastTps"), playerNames), createPlayerResponse(actorServer, playerNames), createPermissionsResponse(actorServerGroup), createEventsResponse((List) reqJson.get("events")) ).setHandler((result) -> { if (result.succeeded()) { + // We don't do anything with the info callback, as + // it's just to update our database. 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()); + ErrorUtils.respondInternalError(ctx, result.cause()); } }); } - public Future> createInfoResponse(Server server, double tps, Map playerNames) { - Future> callback = Future.future(); + // TODO: ASYNC (MAKE ALL SAVES/INSERTS/ETC USED HERE ASYNC + 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(); + callback.complete(); return callback; } 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 d870bfa..e25367b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java @@ -5,7 +5,7 @@ import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.Grant; -import net.frozenorb.apiv3.models.IPLogEntry; +import net.frozenorb.apiv3.models.IpLogEntry; import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.utils.ErrorUtils; @@ -24,7 +24,7 @@ public final class GETUserDetails implements Handler { APIv3.respondJson(ctx, ImmutableMap.builder() .put("user", user) .put("grants", Grant.findByUserSync(user)) - .put("ipLog", IPLogEntry.findByUserSync(user)) + .put("ipLog", IpLogEntry.findByUserSync(user)) .put("punishments", Punishment.findByUserSync(user)) .put("aliases", user.getAliases()) .put("totpSetup", user.getTotpSecret() != null) 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 40de6aa..3c53fdc 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java @@ -2,13 +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 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.IpUtils; import net.frozenorb.apiv3.utils.TOTPUtils; public final class GETUserRequiresTOTP implements Handler { @@ -31,8 +30,8 @@ public final class GETUserRequiresTOTP implements Handler { String userIp = ctx.request().getParam("userIp"); - if (!IPUtils.isValidIP(userIp)) { - ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid."); + if (!IpUtils.isValidIp(userIp)) { + ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); return; } @@ -42,7 +41,7 @@ public final class GETUserRequiresTOTP implements Handler { if (preAuthorizedCallback.get()) { APIv3.respondJson(ctx, ImmutableMap.of( "required", false, - "message", "User's IP has already been validated" + "message", "User's ip has already been validated" )); } else { APIv3.respondJson(ctx, ImmutableMap.of( 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 cfd569e..bdf4fc2 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserConfirmRegister.java @@ -32,22 +32,22 @@ public final class POSTUserConfirmRegister implements Handler { // We can't check email != null as that's set while we're pending // confirmation, we have to check the token. if (user.getEmailToken() == null) { - ErrorUtils.respondGeneric(ctx, "User provided already has email set."); + ErrorUtils.respondGeneric(ctx, 400, "User provided already has email set."); return; } if ((System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) > TimeUnit.DAYS.toMillis(2)) { - ErrorUtils.respondGeneric(ctx, "Email token is expired"); + ErrorUtils.respondGeneric(ctx, 200, "Email token is expired"); return; } String password = ctx.request().getParam("password"); if (password.length() < 8) { - ErrorUtils.respondGeneric(ctx, "Your password is too short."); + ErrorUtils.respondGeneric(ctx, 200, "Your password is too short."); return; } else if (commonPasswords.contains(password)) { - ErrorUtils.respondGeneric(ctx, "Your password is too common. Please use a more secure password."); + ErrorUtils.respondGeneric(ctx, 200, "Your password is too common. Please use a more secure password."); return; } 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 23285d2..f728372 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLeave.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLeave.java @@ -10,16 +10,22 @@ public class POSTUserLeave implements Handler { @Override public void handle(RoutingContext ctx) { - User user = User.findByIdSync(ctx.request().getParam("id")); - - if (user == null) { - ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); - return; - } - - user.leftServer(); - user.save(); - APIv3.respondJson(ctx, user); + User.findById(ctx.request().getParam("id"), ((user, error) -> { + if (error != null) { + ErrorUtils.respondInternalError(ctx, error); + } else if (user == null) { + ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); + } else { + user.leftServer(); + user.save((ignored, error2) -> { + if (error2 != null) { + ErrorUtils.respondInternalError(ctx, error2); + } else { + APIv3.respondJson(ctx, user); + } + }); + } + })); } } \ No newline at end of file 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 2a01d7e..e90b3ba 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java @@ -5,11 +5,12 @@ 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.actors.ServerActor; +import net.frozenorb.apiv3.models.IpLogEntry; import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.utils.ErrorUtils; -import net.frozenorb.apiv3.utils.IPUtils; +import net.frozenorb.apiv3.utils.IpUtils; import net.frozenorb.apiv3.utils.UUIDUtils; import java.util.UUID; @@ -24,9 +25,6 @@ public final class POSTUserLogin implements Handler { return; } - User user = User.findByIdSync(uuid); - String username = ctx.request().getParam("username"); - String userIp = ctx.request().getParam("userIp"); Actor actor = ctx.get("actor"); if (actor.getType() != ActorType.SERVER) { @@ -34,33 +32,53 @@ public final class POSTUserLogin implements Handler { return; } - if (!IPUtils.isValidIP(userIp)) { - ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid."); + String username = ctx.request().getParam("username"); + String userIp = ctx.request().getParam("userIp"); + + if (!IpUtils.isValidIp(userIp)) { + ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); return; } - if (user == null) { - // Will be saved in the User constructor - user = new User(uuid, username); - } + User.findById(uuid, (user, error) -> { + if (error != null) { + ErrorUtils.respondInternalError(ctx, error); + return; + } else if (user == null) { + user = new User(uuid, username); + user.insert(); // TODO + } - Server actorServer = Server.findById(actor.getName()); + User finalUser = user; - IPLogEntry ipLogEntry = IPLogEntry.findByUserAndIpSync(user, userIp); + IpLogEntry.findByUserAndIp(user, userIp, (ipLogEntry, error2) -> { + if (error2 != null) { + ErrorUtils.respondInternalError(ctx, error2); + return; + } else if (ipLogEntry == null) { + ipLogEntry = new IpLogEntry(finalUser, userIp); + ipLogEntry.used(); + ipLogEntry.insert(); //TODO + } else { + ipLogEntry.used(); + ipLogEntry.save(); // TODO + } - // 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)); + finalUser.updateUsername(username, (ignored, error3) -> { + if (error3 != null) { + ErrorUtils.respondInternalError(ctx, error3); + } else { + finalUser.getLoginInfo(((ServerActor) actor).getServer(), (loginInfo, error4) -> { + if (error4 != null) { + ErrorUtils.respondInternalError(ctx, error4); + } else { + APIv3.respondJson(ctx, loginInfo); + } + }); + } + }); + }); + }); } } \ No newline at end of file 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 65ebc15..f2a93b4 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserNotify.java @@ -6,7 +6,6 @@ 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 +23,7 @@ public final class POSTUserNotify implements Handler { } if (user.getEmail() == null) { - ErrorUtils.respondGeneric(ctx, "User provided does not have email set."); + ErrorUtils.respondInvalidInput(ctx, "User provided does not have email set."); return; } @@ -47,20 +46,17 @@ public final class POSTUserNotify implements Handler { bodyReplacements.put(key, values[0]); });*/ - try { - Notification notification = new Notification(template, subjectReplacements, bodyReplacements); + Notification notification = new Notification(template, subjectReplacements, bodyReplacements); - BlockingCallback callback = new BlockingCallback<>(); - notification.sendAsEmail(user.getEmail(), callback); - callback.get(); - - APIv3.respondJson(ctx, ImmutableMap.of( - "success", true - )); - } catch (Exception ex) { - ex.printStackTrace(); - ErrorUtils.respondGeneric(ctx, "Failed to send notification"); - } + notification.sendAsEmail(user.getEmail(), (ignored, error) -> { + if (error != null) { + ErrorUtils.respondInternalError(ctx, error); + } else { + APIv3.respondJson(ctx, ImmutableMap.of( + "success", true + )); + } + }); } } \ No newline at end of file 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 6aec42b..9a28e22 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java @@ -6,7 +6,6 @@ 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; @@ -40,12 +39,12 @@ public final class POSTUserRegister implements Handler { String email = ctx.request().getParam("email"); if (!VALID_EMAIL_PATTERN.matcher(email).matches()) { - ErrorUtils.respondGeneric(ctx, email + " is not a valid email."); + ErrorUtils.respondInvalidInput(ctx, email + " is not a valid email."); return; } if (user.getEmailToken() != null && (System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) < TimeUnit.DAYS.toMillis(2)) { - ErrorUtils.respondGeneric(ctx, "We just recently sent you a confirmation email. Please wait before trying to register again."); + ErrorUtils.respondGeneric(ctx, 200, "We just recently sent you a confirmation email. Please wait before trying to register again."); return; } @@ -62,17 +61,15 @@ public final class POSTUserRegister implements Handler { Notification notification = new Notification(NotificationTemplate.findByIdSync("email-confirmation"), replacements, replacements); - try { - BlockingCallback callback = new BlockingCallback<>(); - notification.sendAsEmail(user.getEmail(), callback); - callback.get(); - APIv3.respondJson(ctx, ImmutableMap.of( - "success", true - )); - } catch (Exception ex) { - ex.printStackTrace(); - ErrorUtils.respondGeneric(ctx, "Failed to send confirmation email. Please contact a MineHQ staff member."); - } + notification.sendAsEmail(user.getEmail(), (ignored, error) -> { + if (error != null) { + ErrorUtils.respondInternalError(ctx, error); + } else { + APIv3.respondJson(ctx, ImmutableMap.of( + "success", true + )); + } + }); } } \ No newline at end of file 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 1725563..386e406 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserSetupTOTP.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserSetupTOTP.java @@ -24,7 +24,7 @@ public final class POSTUserSetupTOTP implements Handler { return; } - GoogleAuthenticatorKey generated = TOTPUtils.generateTOTPKey(); + GoogleAuthenticatorKey generated = TOTPUtils.generateTOTPSecret(); user.setTotpSecret(generated.getKey()); user.save(); 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 82964e6..bf1a163 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserVerifyTOTP.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserVerifyTOTP.java @@ -2,14 +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.IpUtils; import net.frozenorb.apiv3.utils.TOTPUtils; import java.util.concurrent.TimeUnit; @@ -31,8 +29,8 @@ public final class POSTUserVerifyTOTP implements Handler { String userIp = ctx.request().getParam("userIp"); - if (!IPUtils.isValidIP(userIp)) { - ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid."); + if (!IpUtils.isValidIp(userIp)) { + ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); return; } 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 f18d2e7..3ab4719 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/PUTUserMeta.java @@ -26,18 +26,18 @@ public final class PUTUserMeta implements Handler { return; } - Document data = Document.parse(ctx.getBodyAsString()); + Document reqJson = Document.parse(ctx.getBodyAsString()); UserMetaEntry metaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup); if (metaEntry == null) { - metaEntry = new UserMetaEntry(user, serverGroup, data); + metaEntry = new UserMetaEntry(user, serverGroup, reqJson); metaEntry.insert(); } else { - metaEntry.setData(data); + metaEntry.setData(reqJson); metaEntry.save(); } - APIv3.respondJson(ctx, data); + APIv3.respondJson(ctx, reqJson); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/serialization/MineHQCodecProvider.java b/src/main/java/net/frozenorb/apiv3/serialization/MineHQCodecProvider.java deleted file mode 100644 index 3f405aa..0000000 --- a/src/main/java/net/frozenorb/apiv3/serialization/MineHQCodecProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -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/DateTypeAdapter.java b/src/main/java/net/frozenorb/apiv3/serialization/gson/DateTypeAdapter.java similarity index 93% rename from src/main/java/net/frozenorb/apiv3/serialization/DateTypeAdapter.java rename to src/main/java/net/frozenorb/apiv3/serialization/gson/DateTypeAdapter.java index 23373ed..a2c5db7 100644 --- a/src/main/java/net/frozenorb/apiv3/serialization/DateTypeAdapter.java +++ b/src/main/java/net/frozenorb/apiv3/serialization/gson/DateTypeAdapter.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.serialization; +package net.frozenorb.apiv3.serialization.gson; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; diff --git a/src/main/java/net/frozenorb/apiv3/serialization/ExcludeFromReplies.java b/src/main/java/net/frozenorb/apiv3/serialization/gson/ExcludeFromReplies.java similarity index 84% rename from src/main/java/net/frozenorb/apiv3/serialization/ExcludeFromReplies.java rename to src/main/java/net/frozenorb/apiv3/serialization/gson/ExcludeFromReplies.java index eb45468..3e46528 100644 --- a/src/main/java/net/frozenorb/apiv3/serialization/ExcludeFromReplies.java +++ b/src/main/java/net/frozenorb/apiv3/serialization/gson/ExcludeFromReplies.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.serialization; +package net.frozenorb.apiv3.serialization.gson; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/net/frozenorb/apiv3/serialization/FollowAnnotationExclusionStrategy.java b/src/main/java/net/frozenorb/apiv3/serialization/gson/FollowAnnotationExclusionStrategy.java similarity index 89% rename from src/main/java/net/frozenorb/apiv3/serialization/FollowAnnotationExclusionStrategy.java rename to src/main/java/net/frozenorb/apiv3/serialization/gson/FollowAnnotationExclusionStrategy.java index 77d116f..6deee9b 100644 --- a/src/main/java/net/frozenorb/apiv3/serialization/FollowAnnotationExclusionStrategy.java +++ b/src/main/java/net/frozenorb/apiv3/serialization/gson/FollowAnnotationExclusionStrategy.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.serialization; +package net.frozenorb.apiv3.serialization.gson; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; diff --git a/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonDeserializer.java b/src/main/java/net/frozenorb/apiv3/serialization/jackson/UUIDJsonDeserializer.java similarity index 91% rename from src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonDeserializer.java rename to src/main/java/net/frozenorb/apiv3/serialization/jackson/UUIDJsonDeserializer.java index 155737a..35c1af5 100644 --- a/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonDeserializer.java +++ b/src/main/java/net/frozenorb/apiv3/serialization/jackson/UUIDJsonDeserializer.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.serialization; +package net.frozenorb.apiv3.serialization.jackson; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonSerializer.java b/src/main/java/net/frozenorb/apiv3/serialization/jackson/UUIDJsonSerializer.java similarity index 91% rename from src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonSerializer.java rename to src/main/java/net/frozenorb/apiv3/serialization/jackson/UUIDJsonSerializer.java index 5f18aa4..c81e401 100644 --- a/src/main/java/net/frozenorb/apiv3/serialization/UUIDJsonSerializer.java +++ b/src/main/java/net/frozenorb/apiv3/serialization/jackson/UUIDJsonSerializer.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.serialization; +package net.frozenorb.apiv3.serialization.jackson; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/src/main/java/net/frozenorb/apiv3/serialization/mongodb/UUIDCodec.java b/src/main/java/net/frozenorb/apiv3/serialization/mongodb/UUIDCodec.java new file mode 100644 index 0000000..211956b --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/serialization/mongodb/UUIDCodec.java @@ -0,0 +1,28 @@ +package net.frozenorb.apiv3.serialization.mongodb; + +import org.bson.BsonReader; +import org.bson.BsonWriter; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.EncoderContext; + +import java.util.UUID; + +public final class UUIDCodec implements 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; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/serialization/mongodb/UUIDCodecProvider.java b/src/main/java/net/frozenorb/apiv3/serialization/mongodb/UUIDCodecProvider.java new file mode 100644 index 0000000..b5bd0b8 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/serialization/mongodb/UUIDCodecProvider.java @@ -0,0 +1,19 @@ +package net.frozenorb.apiv3.serialization.mongodb; + +import org.bson.codecs.Codec; +import org.bson.codecs.configuration.CodecProvider; +import org.bson.codecs.configuration.CodecRegistry; + +import java.util.UUID; + +public final class UUIDCodecProvider implements CodecProvider { + + public Codec get(Class clazz, CodecRegistry codecRegistry) { + if (clazz == UUID.class) { + return (Codec) new UUIDCodec(); + } else { + return null; + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java index 08ea50b..70e967d 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java @@ -1,30 +1,17 @@ package net.frozenorb.apiv3.unsorted; -import com.cribbstechnologies.clients.mandrill.exception.RequestFailedException; -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.google.common.collect.ImmutableList; +import com.google.common.net.MediaType; import com.mongodb.async.SingleResultCallback; -import com.twilio.sdk.TwilioRestException; -import com.twilio.sdk.resource.factory.MessageFactory; +import io.vertx.core.http.HttpHeaders; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.NotificationTemplate; -import net.frozenorb.apiv3.utils.MandrillUtils; -import net.frozenorb.apiv3.utils.TwillioUtils; -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; +import org.bson.Document; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.Map; public final class Notification { - private static final MandrillMessagesRequest mandrillMessagesRequest = MandrillUtils.createMessagesRequest(); - private static final MessageFactory twillioMessageFactory = TwillioUtils.createMessageFactory(); - private final String subject; private final String body; @@ -33,60 +20,34 @@ public final class Notification { this.body = template.fillBody(bodyReplacements); } - public void sendAsEmail(String email, SingleResultCallback callback) throws IOException { - MandrillHtmlMessage message = new MandrillHtmlMessage(); + public void sendAsEmail(String email, SingleResultCallback callback) { + Document messageJson = new Document(); - message.setFrom_email("no-reply@minehq.com"); - message.setFrom_name("MineHQ Network"); - message.setSubject(subject); - message.setHtml(body); - message.setTo(new MandrillRecipient[] { - new MandrillRecipient(null, email) - }); + messageJson.put("html", body); + messageJson.put("subject", subject); + messageJson.put("from_email", "no-reply@minehq.com"); + messageJson.put("from_name", "MineHQ Network"); + messageJson.put("to", ImmutableList.of( + new Document("email", email).append("name", null).append("type", "to") + )); - 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()) { + Document bodyJson = new Document(); + bodyJson.put("key", APIv3.getConfig().getProperty("mandrill.apiKey")); + bodyJson.put("message", messageJson); + + APIv3.getHttpClient().post("mandrillapp.com", "/api/1.0/messages/send.json", (response) -> { + response.bodyHandler((body) -> { callback.onResult(null, null); - } else { - callback.onResult(null, result.cause()); - } - }); + }); + + response.exceptionHandler((error) -> { + callback.onResult(null, error); + }); + }).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()).end(bodyJson.toJson()); } - 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)); - - 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()); - } - }); + public void sendAsText(String phoneNumber, SingleResultCallback callback) { + callback.onResult(null, new UnsupportedOperationException()); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java b/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java index decc5b5..fb09d76 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java @@ -9,24 +9,27 @@ import net.frozenorb.apiv3.APIv3; public class ErrorUtils { public static void respondServerOnly(RoutingContext ctx) { - respondGeneric(ctx, "This action can only be performed when requested by a server."); + respondGeneric(ctx, 400, "This action can only be performed when requested by a server."); } public static void respondNotFound(RoutingContext ctx, String itemType, String id) { - respondGeneric(ctx, "Not found: " + itemType + " with id " + id + " cannot be found."); + respondGeneric(ctx, 404, "Not found: " + itemType + " with id " + id + " cannot be found."); } public static void respondInvalidInput(RoutingContext ctx, String message) { - respondGeneric(ctx, "Invalid input: " + message + "."); + respondGeneric(ctx, 400, "Invalid input: " + message + "."); } public static void respondRequiredInput(RoutingContext ctx, String field) { - respondGeneric(ctx, "Field \"" + field + "\" is required."); + respondGeneric(ctx, 400, "Field \"" + field + "\" is required."); } - public static void respondGeneric(RoutingContext ctx, String message) { - // TODO: Proper status codes - APIv3.respondJson(ctx, 400, ImmutableMap.of( + public static void respondInternalError(RoutingContext ctx, Throwable error) { + respondGeneric(ctx, 500, "Internal error: " + error.getClass().getSimpleName()); + } + + public static void respondGeneric(RoutingContext ctx, int code, String message) { + APIv3.respondJson(ctx, code, ImmutableMap.of( "success", false, "message", message )); diff --git a/src/main/java/net/frozenorb/apiv3/utils/IPUtils.java b/src/main/java/net/frozenorb/apiv3/utils/IPUtils.java index 7f868e9..b4d9be1 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/IPUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/IPUtils.java @@ -5,7 +5,7 @@ import lombok.experimental.UtilityClass; import java.util.regex.Pattern; @UtilityClass -public class IPUtils { +public class IpUtils { private static final Pattern VALID_IP_PATTERN = Pattern.compile( "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + @@ -13,7 +13,7 @@ public class IPUtils { "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); - public static boolean isValidIP(String ip) { + public static boolean isValidIp(String ip) { return ip != null && VALID_IP_PATTERN.matcher(ip).matches(); } diff --git a/src/main/java/net/frozenorb/apiv3/utils/MandrillUtils.java b/src/main/java/net/frozenorb/apiv3/utils/MandrillUtils.java deleted file mode 100644 index 5ba1de2..0000000 --- a/src/main/java/net/frozenorb/apiv3/utils/MandrillUtils.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.frozenorb.apiv3.utils; - -import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest; -import com.cribbstechnologies.clients.mandrill.request.MandrillRESTRequest; -import com.cribbstechnologies.clients.mandrill.util.MandrillConfiguration; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.experimental.UtilityClass; -import net.frozenorb.apiv3.APIv3; -import org.apache.http.impl.client.HttpClientBuilder; - -@UtilityClass -public class MandrillUtils { - - public static MandrillMessagesRequest createMessagesRequest() { - MandrillConfiguration config = new MandrillConfiguration(); - - config.setApiKey(APIv3.getConfig().getProperty("mandrill.apiKey")); - config.setApiVersion("1.0"); - config.setBaseURL("https://mandrillapp.com/api"); - - MandrillRESTRequest request = new MandrillRESTRequest(); - - request.setConfig(config); - request.setObjectMapper(new ObjectMapper()); - request.setHttpClient(HttpClientBuilder.create().build()); - - MandrillMessagesRequest messagesRequest = new MandrillMessagesRequest(); - messagesRequest.setRequest(request); - return messagesRequest; - } - -} \ 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 f297395..271c219 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java @@ -3,9 +3,9 @@ package net.frozenorb.apiv3.utils; import com.mongodb.async.SingleResultCallback; import lombok.experimental.UtilityClass; import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.unsorted.BlockingCallback; import org.bson.Document; +import java.io.IOException; import java.util.UUID; @UtilityClass @@ -18,19 +18,16 @@ public class MojangUtils { String name = resJson.getString("name"); if (name == null) { - APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.rateLimited"); - callback.onResult(null, new RuntimeException("Hit Mojang API rate limit: " + resJson.toJson())); + callback.onResult(null, new IOException("Hit Mojang API rate limit: " + resJson.toJson())); } else { - APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.success"); callback.onResult(name, null); } }); response.exceptionHandler((error) -> { - APIv3.getStatsD().incrementCounter("apiv3.mojang.sessionServer.failure"); callback.onResult(null, error); }); - }); + }).end(); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java b/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java index 4081811..f097566 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java @@ -1,12 +1,9 @@ package net.frozenorb.apiv3.utils; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import lombok.experimental.UtilityClass; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.Rank; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -66,49 +63,4 @@ public class PermissionUtils { return result; } - public static Map> fromLegacy(String defaultGroup, String customGroup) { - Map> defaultPerms = legacyServerGroupToPerms(APIv3.getGson().fromJson(defaultGroup, HashMap.class)); - Map> customPerms = legacyServerGroupToPerms(APIv3.getGson().fromJson(customGroup, HashMap.class)); - - for (Map.Entry> customPerm : customPerms.entrySet()) { - List original = defaultPerms.get(customPerm.getKey()); - - original.addAll(customPerm.getValue()); - - defaultPerms.put(customPerm.getKey(), new ArrayList<>(ImmutableSet.copyOf(original))); - } - - defaultPerms.remove("coowner"); - defaultPerms.remove("allow-vpns"); - defaultPerms.remove("head-admin"); - defaultPerms.remove("registered"); - defaultPerms.remove("mvp"); - defaultPerms.remove("super-head-admin"); - defaultPerms.remove("trial-mod"); - defaultPerms.remove("forcedeathkick"); - return defaultPerms; - } - - private static Map> legacyServerGroupToPerms(Map group) { - Map> result = new HashMap<>(); - Map permissions = (Map) group.get("permissions"); - - for (Map.Entry entry : permissions.entrySet()) { - List perms = (List) ((Map) entry.getValue()).get("grant"); - String rank = entry.getKey(); - - if (rank.equalsIgnoreCase("unban") || rank.equalsIgnoreCase("pass") || rank.equalsIgnoreCase("pink") || rank.equalsIgnoreCase("jrdev")) { - continue; - } else if (rank.equalsIgnoreCase("high_roller")) { - rank = "high-roller"; - } else if (rank.equalsIgnoreCase("dev")) { - rank = "developer"; - } - - result.put(rank, perms); - } - - return result; - } - } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java b/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java index 1f8b6b3..12feba0 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java @@ -5,44 +5,35 @@ 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 java.util.concurrent.TimeUnit; -import java.util.function.BooleanSupplier; @UtilityClass public class TOTPUtils { private static GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build()); - public static GoogleAuthenticatorKey generateTOTPKey() { + public static GoogleAuthenticatorKey generateTOTPSecret() { return googleAuthenticator.createCredentials(); } public static boolean authorizeUser(User user, int code) { - boolean authorized = googleAuthenticator.authorize(user.getTotpSecret(), code); - - if (!authorized) { - APIv3.getStatsD().incrementCounter("apiv3.totp.failure"); - } - - return authorized; + return googleAuthenticator.authorize(user.getTotpSecret(), code); } - public static String getQRCodeURL(User user, GoogleAuthenticatorKey key) { + public static String getQRCodeURL(User user, GoogleAuthenticatorKey secret) { return GoogleAuthenticatorQRGenerator.getOtpAuthURL( "MineHQ Network", user.getLastUsername(), - key + secret ); } public static void isPreAuthorized(User user, String ip, SingleResultCallback callback) { - APIv3.getRedisClient().exists(user.getId() + ":preAuthorizedIP:" + ip.toLowerCase(), (result) -> { + APIv3.getRedisClient().exists(user.getId() + ":preAuthorizedIp:" + ip.toLowerCase(), (result) -> { if (result.succeeded()) { callback.onResult(result.result() == 1 , null); } else { @@ -52,7 +43,7 @@ public class TOTPUtils { } public static void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback callback) { - String key = user.getId() + ":preAuthorizedIP:" + ip.toLowerCase(); + String key = user.getId() + ":preAuthorizedIp:" + ip.toLowerCase(); APIv3.getRedisClient().set(key, "", (result) -> { if (result.succeeded()) { diff --git a/src/main/java/net/frozenorb/apiv3/utils/TwillioUtils.java b/src/main/java/net/frozenorb/apiv3/utils/TwillioUtils.java deleted file mode 100644 index 9021c6a..0000000 --- a/src/main/java/net/frozenorb/apiv3/utils/TwillioUtils.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.frozenorb.apiv3.utils; - -import com.twilio.sdk.TwilioRestClient; -import com.twilio.sdk.resource.factory.MessageFactory; -import com.twilio.sdk.resource.instance.Account; -import lombok.experimental.UtilityClass; -import net.frozenorb.apiv3.APIv3; - -@UtilityClass -public class TwillioUtils { - - public static MessageFactory createMessageFactory() { - TwilioRestClient twillioClient = new TwilioRestClient( - APIv3.getConfig().getProperty("twillio.accountSID"), - APIv3.getConfig().getProperty("twillio.authToken") - ); - - Account account = twillioClient.getAccount(); - return account.getMessageFactory(); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/UUIDUtils.java b/src/main/java/net/frozenorb/apiv3/utils/UUIDUtils.java index 9a50a64..341dc57 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/UUIDUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/UUIDUtils.java @@ -1,7 +1,6 @@ package net.frozenorb.apiv3.utils; import lombok.experimental.UtilityClass; -import net.frozenorb.apiv3.APIv3; import java.util.UUID; @@ -9,13 +8,7 @@ import java.util.UUID; public class UUIDUtils { public static boolean isAcceptableUUID(UUID uuid) { - boolean acceptable = uuid.version() == 4; - - if (!acceptable) { - APIv3.getStatsD().incrementCounter("apiv3.uuid.invalid"); - } - - return acceptable; + return uuid.version() == 4; } } \ No newline at end of file