Merge pull request #19 from FrozenOrb/experimental

Experimental
This commit is contained in:
Jonathan Halterman 2016-06-19 22:08:30 -07:00 committed by GitHub
commit 8cc20089da
76 changed files with 986 additions and 529 deletions

View File

@ -9,5 +9,7 @@ redis.port=6379
http.port=80 http.port=80
mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ
bugsnag.apiKey=0e47fba8b825416b7cbc839066184509 bugsnag.apiKey=0e47fba8b825416b7cbc839066184509
maxMind.userId=66817
maxMind.maxMindLicenseKey=8Aw9NsOUeOp7
auth.websiteApiKey=RVbp4hY6sCFVaf auth.websiteApiKey=RVbp4hY6sCFVaf
auth.bungeeApiKey=6d9cf76dc9f0d23 auth.bungeeApiKey=6d9cf76dc9f0d23

View File

@ -21,10 +21,7 @@ import com.mongodb.connection.ClusterSettings;
import fr.javatic.mongo.jacksonCodec.JacksonCodecProvider; import fr.javatic.mongo.jacksonCodec.JacksonCodecProvider;
import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory; import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpClient; import io.vertx.core.http.*;
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.Router;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
@ -36,6 +33,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.handler.ActorAttributeHandler; import net.frozenorb.apiv3.handler.ActorAttributeHandler;
import net.frozenorb.apiv3.handler.AuthorizationHandler; import net.frozenorb.apiv3.handler.AuthorizationHandler;
import net.frozenorb.apiv3.handler.LoaderIoHandler;
import net.frozenorb.apiv3.route.GETDump; import net.frozenorb.apiv3.route.GETDump;
import net.frozenorb.apiv3.route.GETWhoAmI; import net.frozenorb.apiv3.route.GETWhoAmI;
import net.frozenorb.apiv3.route.POSTMetrics; import net.frozenorb.apiv3.route.POSTMetrics;
@ -46,6 +44,7 @@ import net.frozenorb.apiv3.route.auditLog.POSTUserAuditLogEntry;
import net.frozenorb.apiv3.route.chatFilterList.GETChatFilterList; import net.frozenorb.apiv3.route.chatFilterList.GETChatFilterList;
import net.frozenorb.apiv3.route.grants.*; import net.frozenorb.apiv3.route.grants.*;
import net.frozenorb.apiv3.route.ipBans.*; import net.frozenorb.apiv3.route.ipBans.*;
import net.frozenorb.apiv3.route.ipIntel.GETIpIntel;
import net.frozenorb.apiv3.route.ipLog.GETUserIpLog; import net.frozenorb.apiv3.route.ipLog.GETUserIpLog;
import net.frozenorb.apiv3.route.notificationTemplates.DELETENotificationTemplate; import net.frozenorb.apiv3.route.notificationTemplates.DELETENotificationTemplate;
import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplate; import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplate;
@ -62,12 +61,14 @@ import net.frozenorb.apiv3.route.serverGroups.GETServerGroups;
import net.frozenorb.apiv3.route.serverGroups.POSTServerGroup; import net.frozenorb.apiv3.route.serverGroups.POSTServerGroup;
import net.frozenorb.apiv3.route.servers.*; import net.frozenorb.apiv3.route.servers.*;
import net.frozenorb.apiv3.route.users.*; import net.frozenorb.apiv3.route.users.*;
import net.frozenorb.apiv3.serialization.gson.DateTypeAdapter;
import net.frozenorb.apiv3.serialization.gson.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.serialization.gson.FollowAnnotationExclusionStrategy;
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonDeserializer; import net.frozenorb.apiv3.serialization.gson.InstantTypeAdapter;
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonSerializer; import net.frozenorb.apiv3.serialization.jackson.InstantJsonDeserializer;
import net.frozenorb.apiv3.serialization.mongodb.UUIDCodecProvider; import net.frozenorb.apiv3.serialization.jackson.InstantJsonSerializer;
import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger; 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 org.bson.Document; import org.bson.Document;
import org.bson.codecs.BsonValueCodecProvider; import org.bson.codecs.BsonValueCodecProvider;
import org.bson.codecs.DocumentCodecProvider; import org.bson.codecs.DocumentCodecProvider;
@ -79,17 +80,22 @@ import org.bson.codecs.configuration.CodecRegistry;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.*; import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
@Slf4j @Slf4j
public final class APIv3 extends AbstractVerticle { public final class APIv3 extends AbstractVerticle {
@Getter private static HttpClient httpClient; @Getter private static HttpClient httpClient;
@Getter private static HttpClient httpsClient;
@Getter private static MongoDatabase database; @Getter private static MongoDatabase database;
@Getter private static Properties config = new Properties(); @Getter private static Properties config = new Properties();
@Getter private static RedisClient redisClient; @Getter private static RedisClient redisClient;
private static final Gson gson = new GsonBuilder() private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateTypeAdapter()) .registerTypeAdapter(Instant.class, new InstantTypeAdapter())
.setExclusionStrategies(new FollowAnnotationExclusionStrategy()) .setExclusionStrategies(new FollowAnnotationExclusionStrategy())
.create(); .create();
@ -160,11 +166,15 @@ public final class APIv3 extends AbstractVerticle {
new IndexModel(new Document("user", 1)), new IndexModel(new Document("user", 1)),
new IndexModel(new Document("user", 1).append("userIp", 1)) new IndexModel(new Document("user", 1).append("userIp", 1))
), (a, b) -> {}); ), (a, b) -> {});
database.getCollection("ipBans").createIndexes(ImmutableList.of(
new IndexModel(new Document("userIp", 1))
), (a, b) -> {});
database.getCollection("punishments").createIndexes(ImmutableList.of( database.getCollection("punishments").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)), new IndexModel(new Document("user", 1)),
new IndexModel(new Document("type", 1)), new IndexModel(new Document("type", 1)),
new IndexModel(new Document("addedAt", 1)), new IndexModel(new Document("addedAt", 1)),
new IndexModel(new Document("addedBy", 1)) new IndexModel(new Document("addedBy", 1)),
new IndexModel(new Document("linkedIpBanId", 1))
), (a, b) -> {}); ), (a, b) -> {});
database.getCollection("users").createIndexes(ImmutableList.of( database.getCollection("users").createIndexes(ImmutableList.of(
new IndexModel(new Document("lastUsername", 1)), new IndexModel(new Document("lastUsername", 1)),
@ -179,8 +189,10 @@ public final class APIv3 extends AbstractVerticle {
ObjectMapper objectMapper = ObjectMapperFactory.createObjectMapper(); ObjectMapper objectMapper = ObjectMapperFactory.createObjectMapper();
SimpleModule simpleModule = new SimpleModule(); SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(UUID.class, new UUIDJsonSerializer()); simpleModule.addSerializer(Instant.class, new InstantJsonSerializer());
simpleModule.addDeserializer(UUID.class, new UUIDJsonDeserializer()); simpleModule.addDeserializer(Instant.class, new InstantJsonDeserializer());
simpleModule.addSerializer(UUID.class, new UuidJsonSerializer());
simpleModule.addDeserializer(UUID.class, new UuidJsonDeserializer());
objectMapper.registerModule(simpleModule); objectMapper.registerModule(simpleModule);
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
@ -190,7 +202,7 @@ public final class APIv3 extends AbstractVerticle {
List<CodecProvider> providers = new ArrayList<>(); List<CodecProvider> providers = new ArrayList<>();
// Our fixed uuid codec // Our fixed uuid codec
providers.add(new UUIDCodecProvider()); providers.add(new UuidCodecProvider());
// Normal providers // Normal providers
providers.add(new ValueCodecProvider()); providers.add(new ValueCodecProvider());
@ -216,7 +228,7 @@ public final class APIv3 extends AbstractVerticle {
Client bugsnag = new Client(config.getProperty("bugsnag.apiKey")); Client bugsnag = new Client(config.getProperty("bugsnag.apiKey"));
bugsnag.setReleaseStage(config.getProperty("general.releaseStage")); bugsnag.setReleaseStage(config.getProperty("general.releaseStage"));
bugsnag.setProjectPackages("net.frozenorb.apiv3"); bugsnag.setProjectPackages("net.frozenorb.apiv3");
bugsnag.setLogger(new BugsnagSLF4JLogger()); bugsnag.setLogger(new BugsnagSlf4jLogger());
} }
// TODO: blockingHandler -> handler // TODO: blockingHandler -> handler
@ -224,6 +236,7 @@ public final class APIv3 extends AbstractVerticle {
HttpServer webServer = vertx.createHttpServer(); HttpServer webServer = vertx.createHttpServer();
Router mainRouter = Router.router(vertx); Router mainRouter = Router.router(vertx);
mainRouter.route().handler(new LoaderIoHandler());
mainRouter.route().handler(new ActorAttributeHandler()); mainRouter.route().handler(new ActorAttributeHandler());
mainRouter.route().handler(new AuthorizationHandler()); mainRouter.route().handler(new AuthorizationHandler());
mainRouter.route().handler(LoggerHandler.create(LoggerFormat.TINY)); mainRouter.route().handler(LoggerHandler.create(LoggerFormat.TINY));
@ -231,79 +244,82 @@ public final class APIv3 extends AbstractVerticle {
// TODO: The commented out routes // TODO: The commented out routes
mainRouter.get("/announcements/:id").blockingHandler(new GETAnnouncements()); mainRouter.get("/announcements/:id").handler(new GETAnnouncements());
mainRouter.put("/announcements/:id").blockingHandler(new PUTAnnouncements()); mainRouter.put("/announcements/:id").handler(new PUTAnnouncements());
mainRouter.get("/auditLog").blockingHandler(new GETAuditLog()); mainRouter.get("/auditLog").handler(new GETAuditLog());
mainRouter.post("/user/:id/auditLogEntry").blockingHandler(new POSTUserAuditLogEntry()); mainRouter.post("/user/:id/auditLogEntry").handler(new POSTUserAuditLogEntry());
mainRouter.get("/chatFilterList").handler(new GETChatFilterList()); mainRouter.get("/chatFilterList").handler(new GETChatFilterList());
mainRouter.get("/grant/:id").blockingHandler(new GETGrant()); mainRouter.get("/grant/:id").handler(new GETGrant());
mainRouter.get("/grants").blockingHandler(new GETGrants()); mainRouter.get("/grants").handler(new GETGrants());
mainRouter.get("/user/:id/grants").blockingHandler(new GETUserGrants()); mainRouter.get("/user/:id/grants").handler(new GETUserGrants());
mainRouter.post("/user/:id/grant").blockingHandler(new POSTUserGrant()); mainRouter.post("/user/:id/grant").blockingHandler(new POSTUserGrant(), false);
mainRouter.delete("/grant/:id").blockingHandler(new DELETEGrant()); mainRouter.delete("/grant/:id").blockingHandler(new DELETEGrant(), false);
mainRouter.get("/ipBan/:id").blockingHandler(new GETIpBan()); mainRouter.get("/ipBan/:id").handler(new GETIpBan());
mainRouter.get("/ipBans").blockingHandler(new GETIpBans()); mainRouter.get("/ipBans").handler(new GETIpBans());
mainRouter.get("/ip/:id/ipBans").blockingHandler(new GETIpIpBans()); mainRouter.get("/ip/:id/ipBans").handler(new GETIpIpBans());
mainRouter.post("/ip/:id/ipBan").blockingHandler(new POSTIpIpBan()); mainRouter.post("/ip/:id/ipBan").blockingHandler(new POSTIpIpBan(), false);
mainRouter.delete("/ipBan/:id").blockingHandler(new DELETEIpBan()); mainRouter.delete("/ipBan/:id").blockingHandler(new DELETEIpBan(), false);
mainRouter.get("/user/:id/ipLog").blockingHandler(new GETUserIpLog()); mainRouter.get("/ip/:id/intel").handler(new GETIpIntel());
mainRouter.get("/notificationTemplate/:id").blockingHandler(new GETNotificationTemplate()); mainRouter.get("/user/:id/ipLog").handler(new GETUserIpLog());
mainRouter.get("/notificationTemplates").blockingHandler(new GETNotificationTemplates());
mainRouter.post("/notificationTemplate").blockingHandler(new POSTNotificationTemplate());
//mainRouter.put("/notificationTemplate/:id").blockingHandler(new PUTNotificationTemplate());
mainRouter.delete("/notificationTemplate/:id").blockingHandler(new DELETENotificationTemplate());
mainRouter.get("/punishment/:id").blockingHandler(new GETPunishment()); mainRouter.get("/notificationTemplate/:id").handler(new GETNotificationTemplate());
mainRouter.get("/punishments").blockingHandler(new GETPunishments()); mainRouter.get("/notificationTemplates").handler(new GETNotificationTemplates());
mainRouter.get("/user/:id/punishments").blockingHandler(new GETUserPunishments()); mainRouter.post("/notificationTemplate").blockingHandler(new POSTNotificationTemplate(), false);
mainRouter.post("/user/:id/punish").blockingHandler(new POSTUserPunish()); //mainRouter.put("/notificationTemplate/:id").blockingHandler(new PUTNotificationTemplate(), false);
mainRouter.delete("/punishment/:id").blockingHandler(new DELETEPunishment()); mainRouter.delete("/notificationTemplate/:id").blockingHandler(new DELETENotificationTemplate(), false);
mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment());
mainRouter.get("/punishment/:id").handler(new GETPunishment());
mainRouter.get("/punishments").handler(new GETPunishments());
mainRouter.get("/user/:id/punishments").handler(new GETUserPunishments());
mainRouter.post("/user/:id/punish").blockingHandler(new POSTUserPunish(), false);
mainRouter.delete("/punishment/:id").blockingHandler(new DELETEPunishment(), false);
mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment(), false);
mainRouter.get("/rank/:id").handler(new GETRank()); mainRouter.get("/rank/:id").handler(new GETRank());
mainRouter.get("/ranks").handler(new GETRanks()); mainRouter.get("/ranks").handler(new GETRanks());
mainRouter.post("/rank").blockingHandler(new POSTRank()); mainRouter.post("/rank").blockingHandler(new POSTRank(), false);
//mainRouter.put("/rank/:id").blockingHandler(new PUTRank()); //mainRouter.put("/rank/:id").blockingHandler(new PUTRank(), false);
mainRouter.delete("/rank/:id").blockingHandler(new DELETERank()); mainRouter.delete("/rank/:id").blockingHandler(new DELETERank(), false);
mainRouter.get("/serverGroup/:id").handler(new GETServerGroup()); mainRouter.get("/serverGroup/:id").handler(new GETServerGroup());
mainRouter.get("/serverGroups").handler(new GETServerGroups()); mainRouter.get("/serverGroups").handler(new GETServerGroups());
mainRouter.post("/serverGroup").blockingHandler(new POSTServerGroup()); mainRouter.post("/serverGroup").blockingHandler(new POSTServerGroup(), false);
//mainRouter.put("/serverGroup/:id").blockingHandler(new PUTServerGroup()); //mainRouter.put("/serverGroup/:id").blockingHandler(new PUTServerGroup(), false);
mainRouter.delete("/serverGroup/:id").blockingHandler(new DELETEServerGroup()); mainRouter.delete("/serverGroup/:id").blockingHandler(new DELETEServerGroup(), false);
mainRouter.get("/server/:id").handler(new GETServer()); mainRouter.get("/server/:id").handler(new GETServer());
mainRouter.get("/servers").handler(new GETServers()); mainRouter.get("/servers").handler(new GETServers());
mainRouter.post("/server/heartbeat").handler(new POSTServerHeartbeat()); mainRouter.post("/server/heartbeat").handler(new POSTServerHeartbeat());
mainRouter.post("/server").blockingHandler(new POSTServer()); mainRouter.post("/server").blockingHandler(new POSTServer(), false);
//mainRouter.put("/server/:id").blockingHandler(new PUTServer()); //mainRouter.put("/server/:id").blockingHandler(new PUTServer(), false);
mainRouter.delete("/server/:id").blockingHandler(new DELETEServer()); mainRouter.delete("/server/:id").blockingHandler(new DELETEServer(), false);
mainRouter.get("/staff").blockingHandler(new GETStaff()); mainRouter.get("/staff").blockingHandler(new GETStaff(), false);
mainRouter.get("/user/:id").blockingHandler(new GETUser()); mainRouter.get("/user/:id").handler(new GETUser());
mainRouter.get("/user/:id/details").blockingHandler(new GETUserDetails()); mainRouter.get("/user/:id/details").blockingHandler(new GETUserDetails(), false);
mainRouter.get("/user/:id/meta/:serverGroup").blockingHandler(new GETUserMeta()); mainRouter.get("/user/:id/meta/:serverGroup").blockingHandler(new GETUserMeta(), false);
mainRouter.get("/user/:id/requiresTOTP").blockingHandler(new GETUserRequiresTOTP()); mainRouter.get("/user/:id/permissions").blockingHandler(new GETUserPermissions(), false);
mainRouter.get("/user/:id/verifyPassword").blockingHandler(new GETUserVerifyPassword()); mainRouter.get("/user/:id/requiresTOTP").blockingHandler(new GETUserRequiresTOTP(), false);
mainRouter.post("/user/confirmRegister/:emailToken").blockingHandler(new POSTUserConfirmRegister()); mainRouter.get("/user/:id/verifyPassword").blockingHandler(new GETUserVerifyPassword(), false);
mainRouter.post("/user/confirmRegister/:emailToken").blockingHandler(new POSTUserConfirmRegister(), false);
mainRouter.post("/user/:id/leave").handler(new POSTUserLeave()); mainRouter.post("/user/:id/leave").handler(new POSTUserLeave());
mainRouter.post("/user/:id/login").handler(new POSTUserLogin()); mainRouter.post("/user/:id/login").blockingHandler(new POSTUserLogin());
mainRouter.post("/user/:id/notify").blockingHandler(new POSTUserNotify()); mainRouter.post("/user/:id/notify").blockingHandler(new POSTUserNotify(), false);
mainRouter.post("/user/:id/register").blockingHandler(new POSTUserRegister()); mainRouter.post("/user/:id/register").blockingHandler(new POSTUserRegister(), false);
mainRouter.post("/user/:id/setupTOTP").blockingHandler(new POSTUserSetupTOTP()); mainRouter.post("/user/:id/setupTOTP").blockingHandler(new POSTUserSetupTOTP(), false);
mainRouter.post("/user/:id/verifyTOTP").blockingHandler(new POSTUserVerifyTOTP()); mainRouter.post("/user/:id/verifyTOTP").blockingHandler(new POSTUserVerifyTOTP(), false);
mainRouter.put("/user/:id/meta/:serverGroup").blockingHandler(new PUTUserMeta()); mainRouter.put("/user/:id/meta/:serverGroup").blockingHandler(new PUTUserMeta(), false);
mainRouter.delete("/user/:id/meta/:serverGroup").blockingHandler(new DELETEUserMeta()); mainRouter.delete("/user/:id/meta/:serverGroup").blockingHandler(new DELETEUserMeta(), false);
mainRouter.get("/dump/:type").handler(new GETDump()); mainRouter.get("/dump/:type").handler(new GETDump());
mainRouter.get("/whoami").handler(new GETWhoAmI()); mainRouter.get("/whoami").handler(new GETWhoAmI());
mainRouter.post("/metrics").blockingHandler(new POSTMetrics()); mainRouter.post("/metrics").handler(new POSTMetrics());
int port = Integer.parseInt(config.getProperty("http.port")); int port = Integer.parseInt(config.getProperty("http.port"));
webServer.requestHandler(mainRouter::accept).listen(port); webServer.requestHandler(mainRouter::accept).listen(port);
@ -311,6 +327,11 @@ public final class APIv3 extends AbstractVerticle {
private void setupHttpClient() { private void setupHttpClient() {
httpClient = vertx.createHttpClient(); httpClient = vertx.createHttpClient();
httpsClient = vertx.createHttpClient(
new HttpClientOptions()
.setSsl(true)
.setTrustAll(true)
);
} }
public static void respondJson(RoutingContext ctx, Object response) { public static void respondJson(RoutingContext ctx, Object response) {

View File

@ -24,6 +24,7 @@ public final class UserActor implements Actor {
if (cachedAuthorized != null) { if (cachedAuthorized != null) {
return cachedAuthorized; return cachedAuthorized;
} else { } else {
// TODO: THIS IS KINDA BLOCKING
boolean authorized = user.hasPermissionAnywhere(Permissions.SIGN_API_REQUEST); boolean authorized = user.hasPermissionAnywhere(Permissions.SIGN_API_REQUEST);
cachedAuthorized = authorized; cachedAuthorized = authorized;
return authorized; return authorized;

View File

@ -56,7 +56,7 @@ public final class V2Importer {
private final Future<T> future; private final Future<T> future;
public FutureCompatibilityCallback(Future<T> future) { private FutureCompatibilityCallback(Future<T> future) {
this.future = future; this.future = future;
} }

View File

@ -7,8 +7,8 @@ import net.frozenorb.apiv3.model.Grant;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -21,6 +21,7 @@ public final class GrantConverter implements Block<Document> {
this.oidToUniqueId = oidToUniqueId; this.oidToUniqueId = oidToUniqueId;
} }
@SuppressWarnings("unchecked")
@Override @Override
public void apply(Document grant) { public void apply(Document grant) {
UUID target = oidToUniqueId.get(((Map<String, Object>) grant.get("target")).get("$id")); UUID target = oidToUniqueId.get(((Map<String, Object>) grant.get("target")).get("$id"));
@ -37,6 +38,8 @@ public final class GrantConverter implements Block<Document> {
rank = "high-roller"; rank = "high-roller";
} else if (rank.equalsIgnoreCase("dev")) { } else if (rank.equalsIgnoreCase("dev")) {
rank = "developer"; rank = "developer";
} else if (rank.equalsIgnoreCase("coowner")) {
rank = "owner";
} }
Grant created = new Grant( Grant created = new Grant(
@ -45,9 +48,9 @@ public final class GrantConverter implements Block<Document> {
grant.containsKey("comment") ? grant.getString("comment") : "", grant.containsKey("comment") ? grant.getString("comment") : "",
grant.containsKey("scope") ? ImmutableSet.copyOf((Collection<String>) grant.get("scope")) : ImmutableSet.of(), grant.containsKey("scope") ? ImmutableSet.copyOf((Collection<String>) grant.get("scope")) : ImmutableSet.of(),
rank, rank,
grant.getDate("expires"), grant.containsKey("expires") ? grant.getDate("expires").toInstant() : null,
grant.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) grant.get("addedBy")).get("$id")) : null, grant.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) grant.get("addedBy")).get("$id")) : null,
grant.containsKey("created") ? grant.getDate("created") : new Date(), grant.containsKey("created") ? grant.getDate("created").toInstant() : Instant.now(),
null, null,
null, null,
null null

View File

@ -20,6 +20,7 @@ public final class IpLogConverter implements Block<Document> {
this.oidToUniqueId = oidToUniqueId; this.oidToUniqueId = oidToUniqueId;
} }
@SuppressWarnings("unchecked")
@Override @Override
public void apply(Document ipLogEntry) { public void apply(Document ipLogEntry) {
UUID user = oidToUniqueId.get(((Map<String, Object>) ipLogEntry.get("user")).get("$id")); UUID user = oidToUniqueId.get(((Map<String, Object>) ipLogEntry.get("user")).get("$id"));
@ -44,10 +45,9 @@ public final class IpLogConverter implements Block<Document> {
new ObjectId().toString(), new ObjectId().toString(),
user, user,
ip, ip,
lastSeen, lastSeen.toInstant(),
lastSeen, lastSeen.toInstant(),
((Number) ipLogEntry.get("uses")).intValue() ((Number) ipLogEntry.get("uses")).intValue()
); );
created.insert(); created.insert();

View File

@ -8,7 +8,6 @@ import net.frozenorb.apiv3.model.Punishment;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -22,6 +21,7 @@ public final class PunishmentConverter implements Block<Document> {
this.oidToUniqueId = oidToUniqueId; this.oidToUniqueId = oidToUniqueId;
} }
@SuppressWarnings("unchecked")
@Override @Override
public void apply(Document punishment) { public void apply(Document punishment) {
UUID target = oidToUniqueId.get(((Map<String, Object>) punishment.get("user")).get("$id")); UUID target = oidToUniqueId.get(((Map<String, Object>) punishment.get("user")).get("$id"));
@ -40,15 +40,15 @@ public final class PunishmentConverter implements Block<Document> {
target, target,
punishment.getString("reason").toString(), punishment.getString("reason").toString(),
Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()), Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()),
punishment.getDate("expires"), punishment.containsKey("expires") ? punishment.getDate("expires").toInstant() : null,
punishment.containsKey("meta") ? (punishment.get("meta") instanceof List ? ImmutableMap.of() : (Document) punishment.get("meta")) : ImmutableMap.of(), punishment.containsKey("meta") ? (punishment.get("meta") instanceof List ? ImmutableMap.of() : (Document) punishment.get("meta")) : ImmutableMap.of(),
null, null,
punishment.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("addedBy")).get("$id")) : null, punishment.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("addedBy")).get("$id")) : null,
(Date) punishment.getDate("created").clone(), punishment.getDate("created").toInstant(),
punishment.containsKey("createdOn") ? String.valueOf(((Map<String, Object>) punishment.get("createdOn")).get("$id")) : "Website", punishment.containsKey("createdOn") ? String.valueOf(((Map<String, Object>) punishment.get("createdOn")).get("$id")) : "Website",
punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE, punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE,
punishment.containsKey("removedBy") ? (((Map<String, Object>) punishment.get("removedBy")).get("$ref").equals("user") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("removedBy")).get("$id")) : null) : null, punishment.containsKey("removedBy") ? (((Map<String, Object>) punishment.get("removedBy")).get("$ref").equals("user") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("removedBy")).get("$id")) : null) : null,
punishment.containsKey("removedBy") ? (punishment.containsKey("removedAt") ? punishment.getDate("removedAt") : punishment.getDate("created")) : null, punishment.containsKey("removedBy") ? (punishment.containsKey("removedAt") ? punishment.getDate("removedAt") : punishment.getDate("created")).toInstant() : null,
punishment.containsKey("removedBy") ? punishment.getString("removalReason").toString() : null punishment.containsKey("removedBy") ? punishment.getString("removalReason").toString() : null
); );

View File

@ -4,7 +4,7 @@ import com.google.common.collect.ImmutableMap;
import com.mongodb.Block; import com.mongodb.Block;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.UUIDUtils; import net.frozenorb.apiv3.util.UuidUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
@ -30,7 +30,7 @@ public final class UserConverter implements Block<Document> {
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")); 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)) { if (!UuidUtils.isAcceptableUuid(uuid)) {
return; return;
} }
@ -39,7 +39,7 @@ public final class UserConverter implements Block<Document> {
User created = new User( User created = new User(
uuid, uuid,
user.get("name").toString(), user.get("name").toString(),
ImmutableMap.of(user.get("name").toString(), user.getDate("joined")), ImmutableMap.of(user.get("name").toString(), user.getDate("joined").toInstant()),
null, null,
null, null,
null, null,
@ -47,8 +47,8 @@ public final class UserConverter implements Block<Document> {
user.getString("email"), user.getString("email"),
user.getString("phone"), user.getString("phone"),
"INVALID", "INVALID",
user.getDate("joined"), user.getDate("joined").toInstant(),
user.getDate("joined"), user.getDate("joined").toInstant(),
false false
); );

View File

@ -9,7 +9,6 @@ import net.frozenorb.apiv3.model.Server;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import java.nio.charset.Charset;
import java.util.Base64; import java.util.Base64;
public final class ActorAttributeHandler implements Handler<RoutingContext> { public final class ActorAttributeHandler implements Handler<RoutingContext> {
@ -120,7 +119,7 @@ public final class ActorAttributeHandler implements Handler<RoutingContext> {
} }
} }
public void processNoAuthorization(RoutingContext ctx) { private void processNoAuthorization(RoutingContext ctx) {
ctx.put("actor", new UnknownActor()); ctx.put("actor", new UnknownActor());
ctx.next(); ctx.next();
} }

View File

@ -0,0 +1,19 @@
package net.frozenorb.apiv3.handler;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
public final class LoaderIoHandler implements Handler<RoutingContext> {
@Override
public void handle(RoutingContext ctx) {
String path = ctx.request().path().replace("/", "");
if (path.equals("loaderio-1c81aa574f79c573e7220e15e30a96aa")) {
ctx.response().end(path);
} else {
ctx.next();
}
}
}

View File

@ -0,0 +1,21 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import net.frozenorb.apiv3.util.MaxMindUtils;
import org.bson.Document;
public final class MaxMindCity {
@Getter private int confidence;
@Getter private int geonameId;
@Getter private String name;
public MaxMindCity() {} // For Jackson
public MaxMindCity(Document legacy) {
this.confidence = legacy.getInteger("confidence");
this.geonameId = legacy.getInteger("geoname_id");
this.name = MaxMindUtils.getEnglishName(legacy);
}
}

View File

@ -0,0 +1,21 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import net.frozenorb.apiv3.util.MaxMindUtils;
import org.bson.Document;
public final class MaxMindContinent {
@Getter private String code;
@Getter private int geonameId;
@Getter private String name;
public MaxMindContinent() {} // For Jackson
public MaxMindContinent(Document legacy) {
this.code = legacy.getString("code");
this.geonameId = legacy.getInteger("geoname_id");
this.name = MaxMindUtils.getEnglishName(legacy);
}
}

View File

@ -0,0 +1,23 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import net.frozenorb.apiv3.util.MaxMindUtils;
import org.bson.Document;
public final class MaxMindCountry {
@Getter private String isoCode;
@Getter private int confidence;
@Getter private int geonameId;
@Getter private String name;
public MaxMindCountry() {} // For Jackson
public MaxMindCountry(Document legacy) {
this.isoCode = legacy.getString("iso_code");
this.confidence = legacy.getInteger("confidence");
this.geonameId = legacy.getInteger("geoname_id");
this.name = MaxMindUtils.getEnglishName(legacy);
}
}

View File

@ -0,0 +1,28 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import org.bson.Document;
public final class MaxMindLocation {
@Getter private double latitude;
@Getter private double longitude;
@Getter private int accuracyRadius;
@Getter private String timeZone;
@Getter private int populationDensity;
@Getter private int metroCode;
@Getter private int averageIncome;
public MaxMindLocation() {} // For Jackson
public MaxMindLocation(Document legacy) {
this.latitude = legacy.getDouble("latitude");
this.longitude = legacy.getDouble("longitude");
this.accuracyRadius = legacy.getInteger("accuracy_radius");
this.timeZone = legacy.getString("time_zone");
this.populationDensity = legacy.getInteger("population_density", -1);
this.metroCode = legacy.getInteger("metro_code", -1); // Metro codes are US only
this.averageIncome = legacy.getInteger("average_income", -1);
}
}

View File

@ -0,0 +1,24 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import org.bson.Document;
public final class MaxMindPostal {
@Getter private String code;
@Getter private int confidence;
public MaxMindPostal() {} // For Jackson
public MaxMindPostal(Document legacy) {
this.code = legacy.getString("code");
// Postal codes aren't guaranteed to exist for all areas
if (code == null) {
code = "";
}
this.confidence = legacy.getInteger("confidence");
}
}

View File

@ -0,0 +1,21 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import net.frozenorb.apiv3.util.MaxMindUtils;
import org.bson.Document;
public final class MaxMindRegisteredCountry {
@Getter private String isoCode;
@Getter private int geonameId;
@Getter private String name;
public MaxMindRegisteredCountry() {} // For Jackson
public MaxMindRegisteredCountry(Document legacy) {
this.isoCode = legacy.getString("iso_code");
this.geonameId = legacy.getInteger("geoname_id");
this.name = MaxMindUtils.getEnglishName(legacy);
}
}

View File

@ -0,0 +1,40 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import org.bson.Document;
import java.util.ArrayList;
import java.util.List;
public final class MaxMindResult {
@Getter private MaxMindContinent continent;
@Getter private MaxMindCity city;
@Getter private MaxMindPostal postal;
@Getter private MaxMindTraits traits;
@Getter private MaxMindLocation location;
@Getter private MaxMindSubdivision[] subdivisions;
@Getter private MaxMindCountry country;
@Getter private MaxMindRegisteredCountry registeredCountry;
public MaxMindResult() {} // For Jackson
public MaxMindResult(Document legacy) {
this.continent = new MaxMindContinent((Document) legacy.get("continent"));
this.city = new MaxMindCity((Document) legacy.get("city"));
this.postal = new MaxMindPostal((Document) legacy.get("postal"));
this.traits = new MaxMindTraits((Document) legacy.get("traits"));
this.location = new MaxMindLocation((Document) legacy.get("location"));
this.country = new MaxMindCountry((Document) legacy.get("country"));
this.registeredCountry = new MaxMindRegisteredCountry((Document) legacy.get("registered_country"));
List<MaxMindSubdivision> subdivisions = new ArrayList<>();
for (Object subdivision : (List<Object>) legacy.get("subdivisions")) {
subdivisions.add(new MaxMindSubdivision((Document) subdivision));
}
this.subdivisions = subdivisions.toArray(new MaxMindSubdivision[subdivisions.size()]);
}
}

View File

@ -0,0 +1,23 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import net.frozenorb.apiv3.util.MaxMindUtils;
import org.bson.Document;
public final class MaxMindSubdivision {
@Getter private String isoCode;
@Getter private int confidence;
@Getter private int geonameId;
@Getter private String name;
public MaxMindSubdivision() {} // For Jackson
public MaxMindSubdivision(Document legacy) {
this.isoCode = legacy.getString("iso_code");
this.confidence = legacy.getInteger("confidence", -1);
this.geonameId = legacy.getInteger("geoname_id");
this.name = MaxMindUtils.getEnglishName(legacy);
}
}

View File

@ -0,0 +1,26 @@
package net.frozenorb.apiv3.maxmind;
import lombok.Getter;
import org.bson.Document;
public final class MaxMindTraits {
@Getter private String isp;
@Getter private String domain;
@Getter private int asn;
@Getter private String asnOrganization;
@Getter private String type; // TODO: MAKE ENUM
@Getter private String organization;
public MaxMindTraits() {} // For Jackson
public MaxMindTraits(Document legacy) {
this.isp = legacy.getString("isp");
this.domain = legacy.getString("domain");
this.asn = legacy.getInteger("autonomous_system_number");
this.asnOrganization = legacy.getString("autonomous_system_organization");
this.type = legacy.getString("user_type");
this.organization = legacy.getString("organization");
}
}

View File

@ -14,7 +14,11 @@ import net.frozenorb.apiv3.util.SyncUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.util.*; import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Entity @Entity
public final class AuditLogEntry { public final class AuditLogEntry {
@ -24,59 +28,31 @@ public final class AuditLogEntry {
@Getter @Id private String id; @Getter @Id private String id;
@Getter private UUID user; @Getter private UUID user;
@Getter private String userIp; @Getter private String userIp;
@Getter private Date performedAt; @Getter private Instant performedAt;
@Getter private String actorName; @Getter private String actorName;
@Getter private ActorType actorType; @Getter private ActorType actorType;
@Getter private AuditLogActionType type; @Getter private AuditLogActionType type;
@Getter private Map<String, Object> metadata; @Getter private Map<String, Object> metadata;
public static List<AuditLogEntry> findAllSync() {
return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", -1)));
}
public static List<AuditLogEntry> findAllPaginatedSync(int skip, int pageSize) {
return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", -1)).skip(skip).limit(pageSize));
}
public static AuditLogEntry findByIdSync(String id) {
return SyncUtils.blockOne(auditLogCollection.find(new Document("_id", id)));
}
public static List<AuditLogEntry> findByUserSync(User user) {
return findByUserSync(user.getId());
}
public static List<AuditLogEntry> findByUserSync(UUID user) { public static List<AuditLogEntry> findByUserSync(UUID user) {
return SyncUtils.blockMulti(auditLogCollection.find(new Document("user", user))); return SyncUtils.blockMulti(auditLogCollection.find(new Document("user", user)));
} }
public static void findAll(SingleResultCallback<List<AuditLogEntry>> callback) {
auditLogCollection.find().sort(new Document("performedAt", -1)).into(new ArrayList<>(), callback);
}
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<AuditLogEntry>> callback) { public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<AuditLogEntry>> callback) {
auditLogCollection.find().sort(new Document("performedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); auditLogCollection.find().sort(new Document("performedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
} }
public static void findById(String id, SingleResultCallback<AuditLogEntry> callback) {
auditLogCollection.find(new Document("_id", id)).first(callback);
}
public static void findByUser(User user, SingleResultCallback<List<AuditLogEntry>> callback) {
findByUser(user.getId(), callback);
}
public static void findByUser(UUID user, SingleResultCallback<List<AuditLogEntry>> callback) { public static void findByUser(UUID user, SingleResultCallback<List<AuditLogEntry>> callback) {
auditLogCollection.find(new Document("user", user)).into(new ArrayList<>(), callback); auditLogCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
} }
public AuditLogEntry() {} // For Morphia public AuditLogEntry() {} // For Jackson
public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map<String, Object> metadata) { public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map<String, Object> metadata) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();
this.user = user.getId(); this.user = user.getId();
this.userIp = userIp; this.userIp = userIp;
this.performedAt = new Date(); this.performedAt = Instant.now();
this.actorName = actor.getName(); this.actorName = actor.getName();
this.actorType = actor.getType(); this.actorType = actor.getType();
this.type = type; this.type = type;

View File

@ -14,6 +14,7 @@ import net.frozenorb.apiv3.util.SyncUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -26,25 +27,21 @@ public final class Grant {
@Getter @Id private String id; @Getter @Id private String id;
@Getter private UUID user; @Getter private UUID user;
@Getter private String reason; @Getter private String reason;
@Getter private Set<String> scopes = new HashSet<>(); // So on things w/o scopes we still load properly (Morphia drops empty sets) @Getter private Set<String> scopes;
@Getter private String rank; @Getter private String rank;
@Getter private Date expiresAt; @Getter private Instant expiresAt;
@Getter private UUID addedBy; @Getter private UUID addedBy;
@Getter private Date addedAt; @Getter private Instant addedAt;
@Getter private UUID removedBy; @Getter private UUID removedBy;
@Getter private Date removedAt; @Getter private Instant removedAt;
@Getter private String removalReason; @Getter private String removalReason;
public static List<Grant> findAllSync() { public static List<Grant> findAllSync() {
return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", -1))); return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", -1)));
} }
public static List<Grant> findAllPaginatedSync(int skip, int pageSize) {
return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize));
}
public static List<Grant> findByRankSync(Collection<Rank> ranks) { public static List<Grant> findByRankSync(Collection<Rank> ranks) {
Collection<String> convertedRanks = ranks.stream().map(Rank::getId).collect(Collectors.toList()); Collection<String> convertedRanks = ranks.stream().map(Rank::getId).collect(Collectors.toList());
return SyncUtils.blockMulti(grantsCollection.find(new Document("rank", new Document("$in", convertedRanks)))); return SyncUtils.blockMulti(grantsCollection.find(new Document("rank", new Document("$in", convertedRanks))));
@ -62,19 +59,10 @@ public final class Grant {
return SyncUtils.blockMulti(grantsCollection.find(new Document("user", user))); return SyncUtils.blockMulti(grantsCollection.find(new Document("user", user)));
} }
public static void findAll(SingleResultCallback<List<Grant>> callback) {
grantsCollection.find().sort(new Document("addedAt", -1)).into(new ArrayList<>(), callback);
}
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Grant>> callback) { public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Grant>> callback) {
grantsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); grantsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
} }
public static void findByRank(Collection<Rank> ranks, SingleResultCallback<List<Grant>> callback) {
Collection<String> 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<Grant> callback) { public static void findById(String id, SingleResultCallback<Grant> callback) {
grantsCollection.find(new Document("_id", id)).first(callback); grantsCollection.find(new Document("_id", id)).first(callback);
} }
@ -107,9 +95,9 @@ public final class Grant {
}); });
} }
public Grant() {} // For Morphia public Grant() {} // For Jackson
public Grant(User user, String reason, Set<ServerGroup> scopes, Rank rank, Date expiresAt, User addedBy) { public Grant(User user, String reason, Set<ServerGroup> scopes, Rank rank, Instant expiresAt, User addedBy) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();
this.user = user.getId(); this.user = user.getId();
this.reason = reason; this.reason = reason;
@ -117,7 +105,7 @@ public final class Grant {
this.rank = rank.getId(); this.rank = rank.getId();
this.expiresAt = expiresAt; this.expiresAt = expiresAt;
this.addedBy = addedBy == null ? null : addedBy.getId(); this.addedBy = addedBy == null ? null : addedBy.getId();
this.addedAt = new Date(); this.addedAt = Instant.now();
} }
public boolean isActive() { public boolean isActive() {
@ -125,11 +113,7 @@ public final class Grant {
} }
public boolean isExpired() { public boolean isExpired() {
if (expiresAt == null) { return expiresAt != null && expiresAt.isBefore(Instant.now());
return false; // Never expires
} else {
return expiresAt.before(new Date());
}
} }
public boolean isRemoved() { public boolean isRemoved() {
@ -152,7 +136,7 @@ public final class Grant {
public void delete(User removedBy, String reason) { public void delete(User removedBy, String reason) {
this.removedBy = removedBy.getId(); this.removedBy = removedBy.getId();
this.removedAt = new Date(); this.removedAt = Instant.now();
this.removalReason = reason; this.removalReason = reason;
BlockingCallback<UpdateResult> callback = new BlockingCallback<>(); BlockingCallback<UpdateResult> callback = new BlockingCallback<>();

View File

@ -16,6 +16,7 @@ import net.frozenorb.apiv3.util.TimeUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.time.Instant;
import java.util.*; import java.util.*;
@Entity @Entity
@ -27,37 +28,21 @@ public final class IpBan {
@Getter @Id private String id; @Getter @Id private String id;
@Getter private String userIp; @Getter private String userIp;
@Getter private String reason; @Getter private String reason;
@Getter private Date expiresAt; @Getter private Instant expiresAt;
@Getter private UUID addedBy; @Getter private UUID addedBy;
@Getter private Date addedAt; @Getter private Instant addedAt;
@Getter private String actorName; @Getter private String actorName;
@Getter private ActorType actorType; @Getter private ActorType actorType;
@Getter private UUID removedBy; @Getter private UUID removedBy;
@Getter private Date removedAt; @Getter private Instant removedAt;
@Getter private String removalReason; @Getter private String removalReason;
public static List<IpBan> findAllSync() {
return SyncUtils.blockMulti(ipBansCollection.find().sort(new Document("addedAt", -1)));
}
public static List<IpBan> 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) { public static IpBan findByIdSync(String id) {
return SyncUtils.blockOne(ipBansCollection.find(new Document("_id", id))); return SyncUtils.blockOne(ipBansCollection.find(new Document("_id", id)));
} }
public static List<IpBan> findByIpSync(String userIp) {
return SyncUtils.blockMulti(ipBansCollection.find(new Document("userIp", userIp)));
}
public static void findAll(SingleResultCallback<List<IpBan>> callback) {
ipBansCollection.find().sort(new Document("addedAt", -1)).into(new ArrayList<>(), callback);
}
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<IpBan>> callback) { public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<IpBan>> callback) {
ipBansCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); ipBansCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
} }
@ -90,7 +75,7 @@ public final class IpBan {
}); });
} }
public IpBan() {} // For Morphia public IpBan() {} // For Jackson
public IpBan(String userIp, Punishment linked) { public IpBan(String userIp, Punishment linked) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();
@ -98,18 +83,18 @@ public final class IpBan {
this.reason = linked.getReason(); this.reason = linked.getReason();
this.expiresAt = linked.getExpiresAt(); this.expiresAt = linked.getExpiresAt();
this.addedBy = linked.getAddedBy(); this.addedBy = linked.getAddedBy();
this.addedAt = new Date(); this.addedAt = Instant.now();
this.actorName = linked.getActorName(); this.actorName = linked.getActorName();
this.actorType = linked.getActorType(); this.actorType = linked.getActorType();
} }
public IpBan(String userIp, String reason, Date expiresAt, User addedBy, Actor actor) { public IpBan(String userIp, String reason, Instant expiresAt, User addedBy, Actor actor) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();
this.userIp = userIp; this.userIp = userIp;
this.reason = reason; this.reason = reason;
this.expiresAt = expiresAt; this.expiresAt = expiresAt;
this.addedBy = addedBy == null ? null : addedBy.getId(); this.addedBy = addedBy == null ? null : addedBy.getId();
this.addedAt = new Date(); this.addedAt = Instant.now();
this.actorName = actor.getName(); this.actorName = actor.getName();
this.actorType = actor.getType(); this.actorType = actor.getType();
} }
@ -119,27 +104,56 @@ public final class IpBan {
} }
public boolean isExpired() { public boolean isExpired() {
if (expiresAt == null) { return expiresAt != null && expiresAt.isBefore(Instant.now());
return false; // Never expires
} else {
return expiresAt.before(new Date());
}
} }
public boolean isRemoved() { public boolean isRemoved() {
return removedBy != null; return removedBy != null;
} }
public String getAccessDenialReason() { // TODO: CLEANUP
String accessDenialReason = "Your ip address has been suspended from the MineHQ Network. \n\n"; public void getAccessDenialReason(SingleResultCallback<String> callback) {
Punishment.findByLinkedIpBanId(id, (punishment, error) -> {
if (error != null) {
callback.onResult(null, error);
return;
}
if (getExpiresAt() != null) { if (punishment != null) {
accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), new Date())); User.findById(punishment.getUser(), (user, error2) -> {
} else { if (error2 != null) {
accessDenialReason += "Appeal at MineHQ.com/appeal"; callback.onResult(null, error2);
} return;
}
return accessDenialReason; String accessDenialReason;
if (user != null) {
accessDenialReason = "Your IP address has been suspended from the MineHQ Network for a punishment related to " + user.getLastUsername() + ". \n\n";
} else {
accessDenialReason = "Your IP address has been suspended from the MineHQ Network. \n\n";
}
if (getExpiresAt() != null) {
accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), Instant.now()));
} else {
accessDenialReason += "Appeal at MineHQ.com/appeal";
}
callback.onResult(accessDenialReason, null);
});
} else {
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(), Instant.now()));
} else {
accessDenialReason += "Appeal at MineHQ.com/appeal";
}
callback.onResult(accessDenialReason, null);
}
});
} }
public void insert() { public void insert() {
@ -150,7 +164,7 @@ public final class IpBan {
public void delete(User removedBy, String reason) { public void delete(User removedBy, String reason) {
this.removedBy = removedBy.getId(); this.removedBy = removedBy.getId();
this.removedAt = new Date(); this.removedAt = Instant.now();
this.removalReason = reason; this.removalReason = reason;
BlockingCallback<UpdateResult> callback = new BlockingCallback<>(); BlockingCallback<UpdateResult> callback = new BlockingCallback<>();

View File

@ -0,0 +1,74 @@
package net.frozenorb.apiv3.model;
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.AllArgsConstructor;
import lombok.Getter;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.maxmind.MaxMindResult;
import net.frozenorb.apiv3.util.MaxMindUtils;
import org.bson.Document;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Entity
@AllArgsConstructor
public final class IpIntel {
private static final MongoCollection<IpIntel> ipIntelCollection = APIv3.getDatabase().getCollection("ipIntel", IpIntel.class);
@Getter @Id private String id;
@Getter private Instant lastUpdatedAt;
@Getter private MaxMindResult result;
public static void findAll(SingleResultCallback<List<IpIntel>> callback) {
ipIntelCollection.find().sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<IpIntel> callback) {
ipIntelCollection.find(new Document("_id", id)).first(callback);
}
public static void findByIdOrInsert(String id, SingleResultCallback<IpIntel> callback) {
findById(id, (existingIpIntel, error) -> {
if (error != null) {
callback.onResult(null, error);
} else if (existingIpIntel != null) {
callback.onResult(existingIpIntel, null);
} else {
MaxMindUtils.getInsights(id, (maxMindResult, error2) -> {
if (error2 != null) {
callback.onResult(null, error2);
} else {
IpIntel newIpIntel = new IpIntel(id, maxMindResult);
newIpIntel.insert((ignored, error3) -> {
if (error3 != null) {
callback.onResult(null, error3);
} else {
callback.onResult(newIpIntel, null);
}
});
}
});
}
});
}
public IpIntel() {} // For Jackson
public IpIntel(String ip, MaxMindResult result) {
this.id = ip;
this.lastUpdatedAt = Instant.now();
this.result = result;
}
public void insert(SingleResultCallback<Void> callback) {
ipIntelCollection.insertOne(this, callback);
}
}

View File

@ -13,8 +13,8 @@ import net.frozenorb.apiv3.util.SyncUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -27,8 +27,8 @@ public final class IpLogEntry {
@Getter @Id private String id; @Getter @Id private String id;
@Getter private UUID user; @Getter private UUID user;
@Getter private String userIp; @Getter private String userIp;
@Getter private Date firstSeenAt; @Getter private Instant firstSeenAt;
@Getter private Date lastSeenAt; @Getter private Instant lastSeenAt;
@Getter private int uses; @Getter private int uses;
public static List<IpLogEntry> findAllSync() { public static List<IpLogEntry> findAllSync() {
@ -79,19 +79,19 @@ public final class IpLogEntry {
ipLogCollection.find(new Document("user", user).append("userIp", userIp)).first(callback); ipLogCollection.find(new Document("user", user).append("userIp", userIp)).first(callback);
} }
public IpLogEntry() {} // For Morphia public IpLogEntry() {} // For Jackson
public IpLogEntry(User user, String userIp) { public IpLogEntry(User user, String userIp) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();
this.user = user.getId(); this.user = user.getId();
this.userIp = userIp; this.userIp = userIp;
this.firstSeenAt = new Date(); this.firstSeenAt = Instant.now();
this.lastSeenAt = new Date(); this.lastSeenAt = Instant.now();
this.uses = 0; this.uses = 0;
} }
public void used() { public void used() {
this.lastSeenAt = new Date(); this.lastSeenAt = Instant.now();
this.uses++; this.uses++;
} }

View File

@ -3,7 +3,6 @@ package net.frozenorb.apiv3.model;
import com.mongodb.async.SingleResultCallback; import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id; import fr.javatic.mongo.jacksonCodec.objectId.Id;
import lombok.Getter; import lombok.Getter;
@ -26,10 +25,6 @@ public final class NotificationTemplate {
@Getter @Setter private String subject; @Getter @Setter private String subject;
@Getter @Setter private String body; @Getter @Setter private String body;
public static List<NotificationTemplate> findAllSync() {
return SyncUtils.blockMulti(notificationTemplatesCollection.find());
}
public static NotificationTemplate findByIdSync(String id) { public static NotificationTemplate findByIdSync(String id) {
return SyncUtils.blockOne(notificationTemplatesCollection.find(new Document("_id", id))); return SyncUtils.blockOne(notificationTemplatesCollection.find(new Document("_id", id)));
} }
@ -42,7 +37,7 @@ public final class NotificationTemplate {
notificationTemplatesCollection.find(new Document("_id", id)).first(callback); notificationTemplatesCollection.find(new Document("_id", id)).first(callback);
} }
public NotificationTemplate() {} // For Morphia public NotificationTemplate() {} // For Jackson
public NotificationTemplate(String id, String subject, String body) { public NotificationTemplate(String id, String subject, String body) {
this.id = id; this.id = id;
@ -75,12 +70,6 @@ public final class NotificationTemplate {
callback.get(); callback.get();
} }
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
notificationTemplatesCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
public void delete() { public void delete() {
BlockingCallback<DeleteResult> callback = new BlockingCallback<>(); BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
notificationTemplatesCollection.deleteOne(new Document("_id", id), callback); notificationTemplatesCollection.deleteOne(new Document("_id", id), callback);

View File

@ -16,6 +16,7 @@ import net.frozenorb.apiv3.util.TimeUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -29,27 +30,19 @@ public final class Punishment {
@Getter private UUID user; @Getter private UUID user;
@Getter private String reason; @Getter private String reason;
@Getter private PunishmentType type; @Getter private PunishmentType type;
@Getter private Date expiresAt; @Getter private Instant expiresAt;
@Getter private Map<String, Object> metadata; @Getter private Map<String, Object> metadata;
@Getter private String linkedIpBanId; @Getter private String linkedIpBanId;
@Getter private UUID addedBy; @Getter private UUID addedBy;
@Getter private Date addedAt; @Getter private Instant addedAt;
@Getter private String actorName; @Getter private String actorName;
@Getter private ActorType actorType; @Getter private ActorType actorType;
@Getter private UUID removedBy; @Getter private UUID removedBy;
@Getter private Date removedAt; @Getter private Instant removedAt;
@Getter private String removalReason; @Getter private String removalReason;
public static List<Punishment> findAllSync() {
return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", -1)));
}
public static List<Punishment> findAllPaginatedSync(int skip, int pageSize) {
return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize));
}
public static List<Punishment> findByTypeSync(Collection<PunishmentType> types) { public static List<Punishment> findByTypeSync(Collection<PunishmentType> types) {
Collection<String> convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList()); Collection<String> convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList());
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("type", new Document("$in", convertedTypes)))); return SyncUtils.blockMulti(punishmentsCollection.find(new Document("type", new Document("$in", convertedTypes))));
@ -76,23 +69,18 @@ public final class Punishment {
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes)))); return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes))));
} }
public static void findAll(SingleResultCallback<List<Punishment>> callback) {
punishmentsCollection.find().sort(new Document("addedAt", -1)).into(new ArrayList<>(), callback);
}
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Punishment>> callback) { public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Punishment>> callback) {
punishmentsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback); punishmentsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
} }
public static void findByType(Collection<PunishmentType> types, SingleResultCallback<List<Punishment>> callback) {
Collection<String> 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<Punishment> callback) { public static void findById(String id, SingleResultCallback<Punishment> callback) {
punishmentsCollection.find(new Document("_id", id)).first(callback); punishmentsCollection.find(new Document("_id", id)).first(callback);
} }
public static void findByLinkedIpBanId(String id, SingleResultCallback<Punishment> callback) {
punishmentsCollection.find(new Document("linkedIpBanId", id)).first(callback);
}
public static void findByUser(User user, SingleResultCallback<List<Punishment>> callback) { public static void findByUser(User user, SingleResultCallback<List<Punishment>> callback) {
findByUser(user.getId(), callback); findByUser(user.getId(), callback);
} }
@ -130,16 +118,16 @@ public final class Punishment {
punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes))).into(new ArrayList<>(), callback); punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes))).into(new ArrayList<>(), callback);
} }
public Punishment() {} // For Morphia public Punishment() {} // For Jackson
public Punishment(User user, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map<String, Object> metadata) { public Punishment(User user, String reason, PunishmentType type, Instant expiresAt, User addedBy, Actor actor, Map<String, Object> metadata) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();
this.user = user.getId(); this.user = user.getId();
this.reason = reason; this.reason = reason;
this.type = type; this.type = type;
this.expiresAt = expiresAt; this.expiresAt = expiresAt;
this.addedBy = addedBy == null ? null : addedBy.getId(); this.addedBy = addedBy == null ? null : addedBy.getId();
this.addedAt = new Date(); this.addedAt = Instant.now();
this.actorName = actor.getName(); this.actorName = actor.getName();
this.actorType = actor.getType(); this.actorType = actor.getType();
this.metadata = metadata; this.metadata = metadata;
@ -150,11 +138,7 @@ public final class Punishment {
} }
public boolean isExpired() { public boolean isExpired() {
if (expiresAt == null) { return expiresAt != null && expiresAt.isBefore(Instant.now());
return false; // Never expires
} else {
return expiresAt.before(new Date());
}
} }
public boolean isRemoved() { public boolean isRemoved() {
@ -169,7 +153,7 @@ public final class Punishment {
String accessDenialReason = "Your account has been suspended from the MineHQ Network. \n\n"; String accessDenialReason = "Your account has been suspended from the MineHQ Network. \n\n";
if (getExpiresAt() != null) { if (getExpiresAt() != null) {
accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), new Date())); accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), Instant.now()));
} else { } else {
accessDenialReason += "Appeal at MineHQ.com/appeal"; accessDenialReason += "Appeal at MineHQ.com/appeal";
} }
@ -181,7 +165,7 @@ public final class Punishment {
} }
public void linkIpBan(IpBan ipBan) { public void linkIpBan(IpBan ipBan) {
this.linkedIpBanId = ipBan.getId();
} }
public void insert() { public void insert() {
@ -192,7 +176,7 @@ public final class Punishment {
public void delete(User removedBy, String reason) { public void delete(User removedBy, String reason) {
this.removedBy = removedBy.getId(); this.removedBy = removedBy.getId();
this.removedAt = new Date(); this.removedAt = Instant.now();
this.removalReason = reason; this.removalReason = reason;
if (linkedIpBanId != null) { if (linkedIpBanId != null) {

View File

@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id; import fr.javatic.mongo.jacksonCodec.objectId.Id;
import lombok.Getter; import lombok.Getter;
@ -45,7 +44,7 @@ public final class Rank {
return rankCache.get(id); return rankCache.get(id);
} }
public Rank() {} // For Morphia public Rank() {} // For Jackson
public Rank(String id, int weight, String displayName, String gameColor, String websiteColor, boolean staffRank) { public Rank(String id, int weight, String displayName, String gameColor, String websiteColor, boolean staffRank) {
this.id = id; this.id = id;
@ -80,12 +79,6 @@ public final class Rank {
callback.get(); callback.get();
} }
public void save() {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
ranksCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
}
public void delete() { public void delete() {
BlockingCallback<DeleteResult> callback = new BlockingCallback<>(); BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
ranksCollection.deleteOne(new Document("_id", id), callback); ranksCollection.deleteOne(new Document("_id", id), callback);

View File

@ -7,13 +7,13 @@ import com.mongodb.client.result.UpdateResult;
import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id; import fr.javatic.mongo.jacksonCodec.objectId.Id;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies; import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies;
import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.SyncUtils;
import org.bson.Document; import org.bson.Document;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -31,9 +31,9 @@ public final class Server {
@Getter @ExcludeFromReplies String apiKey; @Getter @ExcludeFromReplies String apiKey;
@Getter private String serverGroup; @Getter private String serverGroup;
@Getter private String serverIp; @Getter private String serverIp;
@Getter @Setter private Date lastUpdatedAt; @Getter private Instant lastUpdatedAt;
@Getter @Setter private double lastTps; @Getter private double lastTps;
@Getter @Setter @ExcludeFromReplies private Set<UUID> players; @Getter @ExcludeFromReplies private Set<UUID> players;
public static List<Server> findAll() { public static List<Server> findAll() {
updateCacheIfNeeded(); updateCacheIfNeeded();
@ -45,7 +45,7 @@ public final class Server {
return serverCache.get(id); return serverCache.get(id);
} }
public Server() {} // For Morphia public Server() {} // For Jackson
public Server(String id, String displayName, String apiKey, ServerGroup serverGroup, String serverIp) { public Server(String id, String displayName, String apiKey, ServerGroup serverGroup, String serverIp) {
this.id = id; this.id = id;
@ -53,7 +53,7 @@ public final class Server {
this.apiKey = apiKey; this.apiKey = apiKey;
this.serverGroup = serverGroup.getId(); this.serverGroup = serverGroup.getId();
this.serverIp = serverIp; this.serverIp = serverIp;
this.lastUpdatedAt = new Date(); this.lastUpdatedAt = Instant.now();
this.lastTps = 0; this.lastTps = 0;
this.players = new HashSet<>(); this.players = new HashSet<>();
} }
@ -75,7 +75,7 @@ public final class Server {
} }
public void receivedHeartbeat(double tps, Iterable<UUID> players) { public void receivedHeartbeat(double tps, Iterable<UUID> players) {
this.lastUpdatedAt = new Date(); this.lastUpdatedAt = Instant.now();
this.lastTps = tps; this.lastTps = tps;
this.players = ImmutableSet.copyOf(players); this.players = ImmutableSet.copyOf(players);
} }

View File

@ -1,5 +1,6 @@
package net.frozenorb.apiv3.model; package net.frozenorb.apiv3.model;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
@ -28,10 +29,8 @@ public final class ServerGroup {
@Getter @Id private String id; @Getter @Id private String id;
@Getter private String image; @Getter private String image;
// We define these HashSets up here because, in the event they're @Getter @Setter @ExcludeFromReplies private Set<String> announcements;
// empty, Morphia will load them as null, not empty sets. @Getter @Setter @ExcludeFromReplies private Map<String, List<String>> permissions;
@Getter @Setter @ExcludeFromReplies private Set<String> announcements = new HashSet<>();
@Getter @Setter @ExcludeFromReplies private Map<String, List<String>> permissions = new HashMap<>();
// make this and other stuff async // make this and other stuff async
public static List<ServerGroup> findAll() { public static List<ServerGroup> findAll() {
@ -44,7 +43,7 @@ public final class ServerGroup {
return serverGroupCache.get(id); return serverGroupCache.get(id);
} }
public ServerGroup() {} // For Morphia public ServerGroup() {} // For Jackson
public ServerGroup(String id, String image) { public ServerGroup(String id, String image) {
this.id = id; this.id = id;
@ -77,10 +76,8 @@ public final class ServerGroup {
callback.get(); callback.get();
} }
public void save() { public void save(SingleResultCallback<UpdateResult> callback) {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
serverGroupsCollection.replaceOne(new Document("_id", id), this, callback); serverGroupsCollection.replaceOne(new Document("_id", id), this, callback);
callback.get();
} }
public void delete() { public void delete() {

View File

@ -18,15 +18,16 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies; import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies;
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonDeserializer; import net.frozenorb.apiv3.serialization.jackson.UuidJsonDeserializer;
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonSerializer; import net.frozenorb.apiv3.serialization.jackson.UuidJsonSerializer;
import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.util.MojangUtils; import net.frozenorb.apiv3.util.MojangUtils;
import net.frozenorb.apiv3.util.PermissionUtils; import net.frozenorb.apiv3.util.PermissionUtils;
import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.SyncUtils;
import net.frozenorb.apiv3.util.UUIDUtils; import net.frozenorb.apiv3.util.UuidUtils;
import org.bson.Document; import org.bson.Document;
import java.time.Instant;
import java.util.*; import java.util.*;
@Entity @Entity
@ -35,30 +36,26 @@ public final class User {
private static final MongoCollection<User> usersCollection = APIv3.getDatabase().getCollection("users", User.class); private static final MongoCollection<User> usersCollection = APIv3.getDatabase().getCollection("users", User.class);
@Getter @Id @JsonSerialize(using=UUIDJsonSerializer.class) @JsonDeserialize(using=UUIDJsonDeserializer.class) private UUID id; @Getter @Id @JsonSerialize(using=UuidJsonSerializer.class) @JsonDeserialize(using=UuidJsonDeserializer.class) private UUID id;
@Getter private String lastUsername; @Getter private String lastUsername;
@Getter @ExcludeFromReplies private Map<String, Date> aliases = new HashMap<>(); @Getter @ExcludeFromReplies private Map<String, Instant> aliases = new HashMap<>();
@Getter @ExcludeFromReplies @Setter private String totpSecret; @Getter @ExcludeFromReplies @Setter private String totpSecret;
@Getter @ExcludeFromReplies @Setter private String emailToken; @Getter @ExcludeFromReplies @Setter private String emailToken;
@Getter @ExcludeFromReplies @Setter private Date emailTokenSetAt; @Getter @ExcludeFromReplies @Setter private Instant emailTokenSetAt;
@Getter @ExcludeFromReplies private String password; @Getter @ExcludeFromReplies private String password;
@Getter @Setter private String email; @Getter @Setter private String email;
@Getter private String phoneNumber; @Getter private String phoneNumber;
@Getter private String lastSeenOn; @Getter private String lastSeenOn;
@Getter private Date lastSeenAt; @Getter private Instant lastSeenAt;
@Getter private Date firstSeenAt; @Getter private Instant firstSeenAt;
@Getter private boolean online; @Getter private boolean online;
public static List<User> findAllSync() {
return SyncUtils.blockMulti(usersCollection.find().sort(new Document("lastSeenAt", -1)));
}
public static User findByIdSync(String id) { public static User findByIdSync(String id) {
UUID uuid; UUID uuid;
try { try {
uuid = UUID.fromString(id); uuid = UUID.fromString(id);
} catch (IllegalArgumentException ex) { } catch (NullPointerException | IllegalArgumentException ex) {
return null; return null;
} }
@ -66,13 +63,17 @@ public final class User {
} }
public static User findByIdSync(UUID id) { public static User findByIdSync(UUID id) {
if (UUIDUtils.isAcceptableUUID(id)) { if (UuidUtils.isAcceptableUuid(id)) {
return SyncUtils.blockOne(usersCollection.find(new Document("_id", id))); return SyncUtils.blockOne(usersCollection.find(new Document("_id", id)));
} else { } else {
return null; return null;
} }
} }
public static User findByEmailSync(String email) {
return SyncUtils.blockOne(usersCollection.find(new Document("email", email)));
}
public static User findByEmailTokenSync(String emailToken) { public static User findByEmailTokenSync(String emailToken) {
return SyncUtils.blockOne(usersCollection.find(new Document("emailToken", emailToken))); return SyncUtils.blockOne(usersCollection.find(new Document("emailToken", emailToken)));
} }
@ -81,10 +82,6 @@ public final class User {
return SyncUtils.blockOne(usersCollection.find(new Document("lastUsername", lastUsername))); return SyncUtils.blockOne(usersCollection.find(new Document("lastUsername", lastUsername)));
} }
public static void findAll(SingleResultCallback<List<User>> callback) {
usersCollection.find().sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<User> callback) { public static void findById(String id, SingleResultCallback<User> callback) {
try { try {
UUID uuid = UUID.fromString(id); UUID uuid = UUID.fromString(id);
@ -95,7 +92,7 @@ public final class User {
} }
public static void findById(UUID id, SingleResultCallback<User> callback) { public static void findById(UUID id, SingleResultCallback<User> callback) {
if (UUIDUtils.isAcceptableUUID(id)) { if (UuidUtils.isAcceptableUuid(id)) {
usersCollection.find(new Document("_id", id)).first(callback); usersCollection.find(new Document("_id", id)).first(callback);
} else { } else {
callback.onResult(null, null); callback.onResult(null, null);
@ -122,35 +119,31 @@ public final class User {
}); });
} }
public static void findByEmailToken(String emailToken, SingleResultCallback<User> callback) {
usersCollection.find(new Document("emailToken", emailToken)).first(callback);
}
public static void findByLastUsername(String lastUsername, SingleResultCallback<User> callback) { public static void findByLastUsername(String lastUsername, SingleResultCallback<User> callback) {
usersCollection.find(new Document("lastUsername", lastUsername)).first(callback); usersCollection.find(new Document("lastUsername", lastUsername)).first(callback);
} }
public User() {} // For Morphia public User() {} // For Jackson
// TODO: THIS IS CURRENTLY BLOCKING. MAYBE FOR THE HEARTBEAT WE CAN DO SOMETHING
// TO MAKE IT NOT SO BLOCKING
public User(UUID id, String lastUsername) { public User(UUID id, String lastUsername) {
this.id = id; this.id = id;
this.lastUsername = ""; // Intentional, so updateUsername actually does something. this.lastUsername = ""; // Intentional, so updateUsername actually does something.
this.aliases = new HashMap<>(); this.aliases = new HashMap<>();
this.totpSecret = null; this.lastSeenAt = Instant.now();
this.password = null; this.firstSeenAt = Instant.now();
this.email = null;
this.phoneNumber = null;
this.lastSeenOn = null;
this.lastSeenAt = new Date();
this.firstSeenAt = new Date();
// TODO: MAKE THIS ASYNC? SOMEHOW? updateUsername(lastUsername);
BlockingCallback<Void> blockingCallback = new BlockingCallback<>();
updateUsername(lastUsername, blockingCallback);
blockingCallback.get();
} }
public boolean hasPermissionAnywhere(String permission) { public boolean hasPermissionAnywhere(String permission) {
Map<String, Boolean> globalPermissions = getGlobalPermissions();
return globalPermissions.containsKey(permission) && globalPermissions.get(permission);
}
// TODO: ASYNC
public Map<String, Boolean> getGlobalPermissions() {
Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRankAnywhere()); Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRankAnywhere());
for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks().entrySet()) { for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks().entrySet()) {
@ -163,7 +156,7 @@ public final class User {
); );
} }
return globalPermissions.containsKey(permission) && globalPermissions.get(permission); return ImmutableMap.copyOf(globalPermissions);
} }
// TODO: Clean // TODO: Clean
@ -175,7 +168,7 @@ public final class User {
this.lastSeenOn = server.getId(); this.lastSeenOn = server.getId();
if (!online) { if (!online) {
this.lastSeenAt = new Date(); this.lastSeenAt = Instant.now();
} }
this.online = true; this.online = true;
@ -183,35 +176,24 @@ public final class User {
} }
public void leftServer() { public void leftServer() {
this.lastSeenAt = new Date(); this.lastSeenAt = Instant.now();
this.online = false; this.online = false;
} }
public void updateUsername(String newUsername, SingleResultCallback<Void> callback) { public void updateUsername(String newUsername) {
this.aliases.put(newUsername, new Date()); if (!newUsername.equals(lastUsername)) {
this.lastUsername = newUsername;
if (newUsername.equalsIgnoreCase(lastUsername)) { User withNewUsername;
callback.onResult(null, null);
return; while ((withNewUsername = User.findByLastUsernameSync(newUsername)) != null) {
BlockingCallback<String> callback = new BlockingCallback<>();
MojangUtils.getName(withNewUsername.getId(), callback);
withNewUsername.updateUsername(callback.get());
}
} }
this.lastUsername = newUsername; this.aliases.put(newUsername, Instant.now());
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) { public void setPassword(String input) {
@ -231,11 +213,7 @@ public final class User {
} }
public Rank getHighestRankAnywhere() { public Rank getHighestRankAnywhere() {
return getHighestRankScoped(null); return getHighestRankScoped(null, Grant.findByUserSync(this));
}
public Rank getHighestRankScoped(ServerGroup serverGroup) {
return getHighestRankScoped(serverGroup, Grant.findByUserSync(this));
} }
// TODO: Clean // TODO: Clean
@ -377,9 +355,14 @@ public final class User {
"activeBanId", activeBan.getId() "activeBanId", activeBan.getId()
); );
} else if (activeIpBan != null) { } else if (activeIpBan != null) {
// TODO: ASYNC
BlockingCallback<String> callback = new BlockingCallback<>();
activeIpBan.getAccessDenialReason(callback);
String reason = callback.get();
access = ImmutableMap.of( access = ImmutableMap.of(
"allowed", false, "allowed", false,
"message", activeIpBan.getAccessDenialReason(), "message", reason,
"activeIpBanId", activeIpBan.getId() "activeIpBanId", activeIpBan.getId()
); );
} }

View File

@ -1,7 +1,6 @@
package net.frozenorb.apiv3.model; package net.frozenorb.apiv3.model;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
@ -15,8 +14,6 @@ import net.frozenorb.apiv3.util.SyncUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -30,14 +27,6 @@ public final class UserMetaEntry {
@Getter private String serverGroup; @Getter private String serverGroup;
@Getter @Setter private Map<String, Object> data; @Getter @Setter private Map<String, Object> data;
public static List<UserMetaEntry> findAllSync() {
return SyncUtils.blockMulti(userMetaCollection.find());
}
public static UserMetaEntry findByIdSync(String id) {
return SyncUtils.blockOne(userMetaCollection.find(new Document("_id", id)));
}
public static UserMetaEntry findByUserAndGroupSync(User user, ServerGroup serverGroup) { public static UserMetaEntry findByUserAndGroupSync(User user, ServerGroup serverGroup) {
return findByUserAndGroupSync(user.getId(), serverGroup); return findByUserAndGroupSync(user.getId(), serverGroup);
} }
@ -46,23 +35,7 @@ public final class UserMetaEntry {
return SyncUtils.blockOne(userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId()))); return SyncUtils.blockOne(userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())));
} }
public static void findAll(SingleResultCallback<List<UserMetaEntry>> callback) { public UserMetaEntry() {} // For Jackson
userMetaCollection.find().into(new ArrayList<>(), callback);
}
public static void findById(String id, SingleResultCallback<UserMetaEntry> callback) {
userMetaCollection.find(new Document("_id", id)).first(callback);
}
public static void findByUserAndGroup(User user, ServerGroup serverGroup, SingleResultCallback<UserMetaEntry> callback) {
findByUserAndGroup(user.getId(), serverGroup, callback);
}
public static void findByUserAndGroup(UUID user, ServerGroup serverGroup, SingleResultCallback<UserMetaEntry> callback) {
userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())).first(callback);
}
public UserMetaEntry() {} // For Morphia
public UserMetaEntry(User user, ServerGroup serverGroup, Map<String, Object> data) { public UserMetaEntry(User user, ServerGroup serverGroup, Map<String, Object> data) {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();

View File

@ -108,7 +108,6 @@ public final class GETDump implements Handler<RoutingContext> {
return; return;
default: default:
ErrorUtils.respondInvalidInput(ctx, type + " is not a valid type. Not in [ban, blacklist, accessDeniable, grant]"); ErrorUtils.respondInvalidInput(ctx, type + " is not a valid type. Not in [ban, blacklist, accessDeniable, grant]");
return;
} }
} }

View File

@ -13,10 +13,9 @@ public final class GETAnnouncements implements Handler<RoutingContext> {
if (serverGroup == null) { if (serverGroup == null) {
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id"));
return; } else {
APIv3.respondJson(ctx, serverGroup.getAnnouncements());
} }
APIv3.respondJson(ctx, serverGroup.getAnnouncements());
} }
} }

View File

@ -16,18 +16,22 @@ public final class PUTAnnouncements implements Handler<RoutingContext> {
if (serverGroup == null) { if (serverGroup == null) {
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id"));
return; } else {
Set<String> announcements = new HashSet<>();
for (Object announcement : ctx.getBodyAsJsonArray()) {
announcements.add((String) announcement);
}
serverGroup.setAnnouncements(announcements);
serverGroup.save((ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, serverGroup.getAnnouncements());
}
});
} }
Set<String> announcements = new HashSet<>();
for (Object announcement : ctx.getBodyAsJsonArray()) {
announcements.add((String) announcement);
}
serverGroup.setAnnouncements(announcements);
serverGroup.save();
APIv3.respondJson(ctx, serverGroup.getAnnouncements());
} }
} }

View File

@ -13,7 +13,13 @@ public final class GETAuditLog implements Handler<RoutingContext> {
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip")); 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")); int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
APIv3.respondJson(ctx, AuditLogEntry.findAllPaginatedSync(skip, pageSize)); AuditLogEntry.findAllPaginated(skip, pageSize, (auditLog, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, auditLog);
}
});
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
} }

View File

@ -5,9 +5,7 @@ import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.auditLog.AuditLogActionType;
import net.frozenorb.apiv3.model.AuditLogEntry;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.IpUtils;
import org.bson.Document; import org.bson.Document;
@ -15,33 +13,37 @@ import org.bson.Document;
public final class POSTUserAuditLogEntry implements Handler<RoutingContext> { public final class POSTUserAuditLogEntry implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.findByIdSync(ctx.request().getParam("id")); 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 {
String userIp = ctx.request().getParam("userIp");
if (user == null) { if (!IpUtils.isValidIp(userIp)) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
return; return;
} }
String userIp = ctx.request().getParam("userIp"); AuditLogActionType type;
if (!IpUtils.isValidIp(userIp)) { try {
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); type = AuditLogActionType.valueOf(ctx.request().getParam("type"));
return; } catch (IllegalArgumentException ex) {
} ErrorUtils.respondNotFound(ctx, "Audit log action type", ctx.request().getParam("type"));
return;
}
AuditLogActionType type; AuditLog.log(user, userIp, ctx.get("actor"), type, Document.parse(ctx.getBodyAsString()), (auditLogEntry, error2) -> {
if (error2 != null) {
try { ErrorUtils.respondInternalError(ctx, error2);
type = AuditLogActionType.valueOf(ctx.request().getParam("type")); } else {
} catch (IllegalArgumentException ex) { APIv3.respondJson(ctx, auditLogEntry);
ErrorUtils.respondNotFound(ctx, "Audit log action type", ctx.request().getParam("type")); }
return; });
} }
});
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
AuditLog.log(user, userIp, ctx.get("actor"), type, Document.parse(ctx.getBodyAsString()), blockingCallback);
AuditLogEntry entry = blockingCallback.get();
APIv3.respondJson(ctx, entry);
} }
} }

View File

@ -4,11 +4,18 @@ import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.Grant; import net.frozenorb.apiv3.model.Grant;
import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETGrant implements Handler<RoutingContext> { public final class GETGrant implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, Grant.findByIdSync(ctx.request().getParam("id"))); Grant.findById(ctx.request().getParam("id"), (grant, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, grant);
}
});
} }
} }

View File

@ -13,7 +13,13 @@ public final class GETGrants implements Handler<RoutingContext> {
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip")); 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")); int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
APIv3.respondJson(ctx, Grant.findAllPaginatedSync(skip, pageSize)); Grant.findAllPaginated(skip, pageSize, (grants, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, grants);
}
});
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
} }

View File

@ -10,14 +10,21 @@ import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETUserGrants implements Handler<RoutingContext> { public final class GETUserGrants implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.findByIdSync(ctx.request().getParam("id")); User.findById(ctx.request().getParam("id"), (user, error) -> {
if (error != null) {
if (target == null) { ErrorUtils.respondInternalError(ctx, error);
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); } else if (user == null) {
return; ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
} } else {
Grant.findByUser(user, (grants, error2) -> {
APIv3.respondJson(ctx, Grant.findByUserSync(target)); if (error2 != null) {
ErrorUtils.respondInternalError(ctx, error2);
} else {
APIv3.respondJson(ctx, grants);
}
});
}
});
} }
} }

View File

@ -9,7 +9,7 @@ import net.frozenorb.apiv3.model.ServerGroup;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import java.util.Date; import java.time.Instant;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -53,16 +53,16 @@ public final class POSTUserGrant implements Handler<RoutingContext> {
return; return;
} }
Date expiresAt; Instant expiresAt = null;
try { try {
expiresAt = new Date(Long.parseLong(ctx.request().getParam("expiresAt"))); expiresAt = Instant.ofEpochMilli(Long.parseLong(ctx.request().getParam("expiresAt")));
} catch (NumberFormatException ex) { } catch (NumberFormatException ignored) {
expiresAt = null; // Just leave it null, we don't need an expiration date.
} }
if (expiresAt != null && expiresAt.before(new Date())) { if (expiresAt != null && expiresAt.isBefore(Instant.now())) {
ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past."); ErrorUtils.respondInvalidInput(ctx, "Expiration time cannot be in the past.");
return; return;
} }

View File

@ -4,11 +4,18 @@ import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.IpBan; import net.frozenorb.apiv3.model.IpBan;
import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETIpBan implements Handler<RoutingContext> { public final class GETIpBan implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, IpBan.findByIdSync(ctx.request().getParam("id"))); IpBan.findById(ctx.request().getParam("id"), (ipBan, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, ipBan);
}
});
} }
} }

View File

@ -13,7 +13,13 @@ public final class GETIpBans implements Handler<RoutingContext> {
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip")); 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")); int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
APIv3.respondJson(ctx, IpBan.findAllPaginatedSync(skip, pageSize)); IpBan.findAllPaginated(skip, pageSize, (grants, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, grants);
}
});
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
} }

View File

@ -17,7 +17,13 @@ public final class GETIpIpBans implements Handler<RoutingContext> {
return; return;
} }
APIv3.respondJson(ctx, IpBan.findByIpSync(userIp)); IpBan.findByIp(userIp, (ipBans, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, ipBans);
}
});
} }
} }

View File

@ -8,7 +8,7 @@ import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.IpUtils;
import java.util.Date; import java.time.Instant;
public final class POSTIpIpBan implements Handler<RoutingContext> { public final class POSTIpIpBan implements Handler<RoutingContext> {
@ -27,16 +27,16 @@ public final class POSTIpIpBan implements Handler<RoutingContext> {
return; return;
} }
Date expiresAt; Instant expiresAt = null;
try { try {
expiresAt = new Date(Long.parseLong(ctx.request().getParam("expiresAt"))); expiresAt = Instant.ofEpochMilli(Long.parseLong(ctx.request().getParam("expiresAt")));
} catch (NumberFormatException ex) { } catch (NumberFormatException ignored) {
expiresAt = null; // Just leave it null, we don't need an expiration date.
} }
if (expiresAt != null && expiresAt.before(new Date())) { if (expiresAt != null && expiresAt.isBefore(Instant.now())) {
ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past."); ErrorUtils.respondInvalidInput(ctx, "Expiration time cannot be in the past.");
return; return;
} }

View File

@ -0,0 +1,29 @@
package net.frozenorb.apiv3.route.ipIntel;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.IpIntel;
import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.IpUtils;
public final class GETIpIntel implements Handler<RoutingContext> {
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;
}
IpIntel.findByIdOrInsert(userIp, (ipIntel, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, ipIntel);
}
});
}
}

View File

@ -10,14 +10,21 @@ import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETUserIpLog implements Handler<RoutingContext> { public final class GETUserIpLog implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.findByIdSync(ctx.request().getParam("id")); User.findById(ctx.request().getParam("id"), (user, error) -> {
if (error != null) {
if (target == null) { ErrorUtils.respondInternalError(ctx, error);
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); } else if (user == null) {
return; ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
} } else {
IpLogEntry.findByUser(user, (ipLog, error2) -> {
APIv3.respondJson(ctx, IpLogEntry.findByUserSync(target)); if (error2 != null) {
ErrorUtils.respondInternalError(ctx, error2);
} else {
APIv3.respondJson(ctx, ipLog);
}
});
}
});
} }
} }

View File

@ -4,11 +4,18 @@ import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.NotificationTemplate;
import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETNotificationTemplate implements Handler<RoutingContext> { public final class GETNotificationTemplate implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, NotificationTemplate.findByIdSync(ctx.request().getParam("id"))); NotificationTemplate.findById(ctx.request().getParam("id"), (notificationTemplate, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, notificationTemplate);
}
});
} }
} }

View File

@ -4,11 +4,18 @@ import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.NotificationTemplate;
import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETNotificationTemplates implements Handler<RoutingContext> { public final class GETNotificationTemplates implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, NotificationTemplate.findAllSync()); NotificationTemplate.findAll((notificationTemplates, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, notificationTemplates);
}
});
} }
} }

View File

@ -4,11 +4,18 @@ import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.Punishment; import net.frozenorb.apiv3.model.Punishment;
import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETPunishment implements Handler<RoutingContext> { public final class GETPunishment implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, Punishment.findByIdSync(ctx.request().getParam("id"))); Punishment.findById(ctx.request().getParam("id"), (punishment, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, punishment);
}
});
} }
} }

View File

@ -13,7 +13,13 @@ public final class GETPunishments implements Handler<RoutingContext> {
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip")); 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")); int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
APIv3.respondJson(ctx, Punishment.findAllPaginatedSync(skip, pageSize)); Punishment.findAllPaginated(skip, pageSize, (grants, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, grants);
}
});
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs."); ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
} }

View File

@ -10,14 +10,21 @@ import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETUserPunishments implements Handler<RoutingContext> { public final class GETUserPunishments implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User target = User.findByIdSync(ctx.request().getParam("id")); User.findById(ctx.request().getParam("id"), (user, error) -> {
if (error != null) {
if (target == null) { ErrorUtils.respondInternalError(ctx, error);
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); } else if (user == null) {
return; ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
} } else {
Punishment.findByUser(user, (punishments, error2) -> {
APIv3.respondJson(ctx, Punishment.findByUserSync(target)); if (error2 != null) {
ErrorUtils.respondInternalError(ctx, error2);
} else {
APIv3.respondJson(ctx, punishments);
}
});
}
});
} }
} }

View File

@ -12,7 +12,7 @@ import net.frozenorb.apiv3.unsorted.Permissions;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import org.bson.Document; import org.bson.Document;
import java.util.Date; import java.time.Instant;
import java.util.Map; import java.util.Map;
public final class POSTUserPunish implements Handler<RoutingContext> { public final class POSTUserPunish implements Handler<RoutingContext> {
@ -43,16 +43,16 @@ public final class POSTUserPunish implements Handler<RoutingContext> {
} }
} }
Date expiresAt; Instant expiresAt = null;
try { try {
expiresAt = new Date(Long.parseLong(ctx.request().getParam("expiresAt"))); expiresAt = Instant.ofEpochMilli(Long.parseLong(ctx.request().getParam("expiresAt")));
} catch (NumberFormatException ex) { } catch (NumberFormatException ignored) {
expiresAt = null; // Just leave it null, we don't need an expiration date.
} }
if (expiresAt != null && expiresAt.before(new Date())) { if (expiresAt != null && expiresAt.isBefore(Instant.now())) {
ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past."); ErrorUtils.respondInvalidInput(ctx, "Expiration time cannot be in the past.");
return; return;
} }
@ -73,7 +73,7 @@ public final class POSTUserPunish implements Handler<RoutingContext> {
Punishment punishment = new Punishment(target, reason, type, expiresAt, addedBy, ctx.get("actor"), meta); Punishment punishment = new Punishment(target, reason, type, expiresAt, addedBy, ctx.get("actor"), meta);
String accessDenialReason = punishment.getAccessDenialReason(); String accessDenialReason = punishment.getAccessDenialReason();
String userIp = ctx.request().getParam("userIp"); String userIp = ctx.request().getParam("playerIp"); // TODO: YELL AT GRIFFIN FOR THIS, IT SHOULD BE USERIP
if (userIp != null) { if (userIp != null) {
IpBan ipBan = new IpBan(userIp, punishment); IpBan ipBan = new IpBan(userIp, punishment);

View File

@ -13,10 +13,13 @@ import net.frozenorb.apiv3.actor.ActorType;
import net.frozenorb.apiv3.model.*; import net.frozenorb.apiv3.model.*;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.PermissionUtils; import net.frozenorb.apiv3.util.PermissionUtils;
import net.frozenorb.apiv3.util.UUIDUtils; import net.frozenorb.apiv3.util.UuidUtils;
import org.bson.Document; import org.bson.Document;
import java.util.*; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Slf4j @Slf4j
public final class POSTServerHeartbeat implements Handler<RoutingContext> { public final class POSTServerHeartbeat implements Handler<RoutingContext> {
@ -55,19 +58,17 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
} }
// TODO: ASYNC (MAKE ALL SAVES/INSERTS/ETC USED HERE ASYNC // TODO: ASYNC (MAKE ALL SAVES/INSERTS/ETC USED HERE ASYNC
public Future<Void> createInfoResponse(Server server, double tps, Map<UUID, String> playerNames) { private Future<Void> createInfoResponse(Server server, double tps, Map<UUID, String> playerNames) {
Future<Void> callback = Future.future(); Future<Void> callback = Future.future();
server.setPlayers(playerNames.keySet()); server.receivedHeartbeat(tps, playerNames.keySet());
server.setLastTps(tps);
server.setLastUpdatedAt(new Date());
server.save(); server.save();
callback.complete(); callback.complete();
return callback; return callback;
} }
public Future<Map<String, Object>> createPlayerResponse(Server server, Map<UUID, String> playerNames) { private Future<Map<String, Object>> createPlayerResponse(Server server, Map<UUID, String> playerNames) {
Future<Map<String, Object>> callback = Future.future(); Future<Map<String, Object>> callback = Future.future();
Future<Map<UUID, User>> userLookupCallback = Future.future(); Future<Map<UUID, User>> userLookupCallback = Future.future();
@ -138,7 +139,7 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
return callback; return callback;
} }
public Future<Map<String, Object>> createPermissionsResponse(ServerGroup serverGroup) { private Future<Map<String, Object>> createPermissionsResponse(ServerGroup serverGroup) {
Future<Map<String, Object>> callback = Future.future(); Future<Map<String, Object>> callback = Future.future();
Map<String, Object> permissionsResponse = new HashMap<>(); Map<String, Object> permissionsResponse = new HashMap<>();
@ -155,7 +156,7 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
return callback; return callback;
} }
public Future<Map<String, Object>> createEventsResponse(List<Object> eventsData) { private Future<Map<String, Object>> createEventsResponse(List<Object> eventsData) {
Future<Map<String, Object>> callback = Future.future(); Future<Map<String, Object>> callback = Future.future();
for (Object event : eventsData) { for (Object event : eventsData) {
@ -178,7 +179,7 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
return callback; return callback;
} }
public Map<UUID, String> extractPlayerNames(Document reqJson) { private Map<UUID, String> extractPlayerNames(Document reqJson) {
Map<UUID, String> result = new HashMap<>(); Map<UUID, String> result = new HashMap<>();
for (Object player : (List<Object>) reqJson.get("players")) { for (Object player : (List<Object>) reqJson.get("players")) {
@ -186,7 +187,7 @@ public final class POSTServerHeartbeat implements Handler<RoutingContext> {
UUID uuid = UUID.fromString(playerJson.getString("uuid")); UUID uuid = UUID.fromString(playerJson.getString("uuid"));
String username = playerJson.getString("username"); String username = playerJson.getString("username");
if (UUIDUtils.isAcceptableUUID(uuid)) { if (UuidUtils.isAcceptableUuid(uuid)) {
result.put(uuid, username); result.put(uuid, username);
} }
} }

View File

@ -4,11 +4,18 @@ import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETUser implements Handler<RoutingContext> { public final class GETUser implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, User.findByIdSync(ctx.request().getParam("id"))); User.findById(ctx.request().getParam("id"), (user, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, user);
}
});
} }
} }

View File

@ -0,0 +1,22 @@
package net.frozenorb.apiv3.route.users;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.ErrorUtils;
public final class GETUserPermissions implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
User target = User.findByIdSync(ctx.request().getParam("id"));
if (target == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
}
APIv3.respondJson(ctx, target.getGlobalPermissions());
}
}

View File

@ -8,7 +8,7 @@ import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.IpUtils;
import net.frozenorb.apiv3.util.TOTPUtils; import net.frozenorb.apiv3.util.TotpUtils;
public final class GETUserRequiresTOTP implements Handler<RoutingContext> { public final class GETUserRequiresTOTP implements Handler<RoutingContext> {
@ -36,7 +36,7 @@ public final class GETUserRequiresTOTP implements Handler<RoutingContext> {
} }
BlockingCallback<Boolean> preAuthorizedCallback = new BlockingCallback<>(); BlockingCallback<Boolean> preAuthorizedCallback = new BlockingCallback<>();
TOTPUtils.isPreAuthorized(user, userIp, preAuthorizedCallback); TotpUtils.isPreAuthorized(user, userIp, preAuthorizedCallback);
if (preAuthorizedCallback.get()) { if (preAuthorizedCallback.get()) {
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(

View File

@ -12,6 +12,14 @@ public final class GETUserVerifyPassword implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User user = User.findByIdSync(ctx.request().getParam("id")); User user = User.findByIdSync(ctx.request().getParam("id"));
if (user == null) {
user = User.findByLastUsernameSync(ctx.request().getParam("id"));
}
if (user == null) {
user = User.findByEmailSync(ctx.request().getParam("id"));
}
if (user == null) { if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return; return;

View File

@ -36,7 +36,7 @@ public final class POSTUserConfirmRegister implements Handler<RoutingContext> {
return; return;
} }
if ((System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) > TimeUnit.DAYS.toMillis(2)) { if ((System.currentTimeMillis() - user.getEmailTokenSetAt().toEpochMilli()) > TimeUnit.DAYS.toMillis(2)) {
ErrorUtils.respondGeneric(ctx, 200, "Email token is expired"); ErrorUtils.respondGeneric(ctx, 200, "Email token is expired");
return; return;
} }

View File

@ -11,7 +11,7 @@ import net.frozenorb.apiv3.model.Server;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.IpUtils;
import net.frozenorb.apiv3.util.UUIDUtils; import net.frozenorb.apiv3.util.UuidUtils;
import java.util.UUID; import java.util.UUID;
@ -20,11 +20,14 @@ public final class POSTUserLogin implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
UUID uuid = UUID.fromString(ctx.request().getParam("id")); UUID uuid = UUID.fromString(ctx.request().getParam("id"));
if (!UUIDUtils.isAcceptableUUID(uuid)) { if (!UuidUtils.isAcceptableUuid(uuid)) {
ErrorUtils.respondInvalidInput(ctx, "UUID \"" + uuid + "\" is not valid - must be version 4 UUID."); ErrorUtils.respondInvalidInput(ctx, "UUID \"" + uuid + "\" is not valid - must be version 4 UUID.");
return; return;
} }
User user = User.findByIdSync(uuid);
String username = ctx.request().getParam("username");
String userIp = ctx.request().getParam("userIp");
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");
Server server; Server server;
@ -39,52 +42,36 @@ public final class POSTUserLogin implements Handler<RoutingContext> {
} }
} }
String username = ctx.request().getParam("username");
String userIp = ctx.request().getParam("userIp");
if (!IpUtils.isValidIp(userIp)) { if (!IpUtils.isValidIp(userIp)) {
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid.");
return; return;
} }
User.findById(uuid, (user, error) -> { if (user == null) {
// Will be saved in the User constructor
user = new User(uuid, username);
}
IpLogEntry ipLogEntry = IpLogEntry.findByUserAndIpSync(user, userIp);
// We use a little bit more verbose code here to save on the
// overhead of a .insert() immediately followed by a .save()
if (ipLogEntry == null) {
ipLogEntry = new IpLogEntry(user, userIp);
ipLogEntry.used();
ipLogEntry.insert();
} else {
ipLogEntry.used();
ipLogEntry.save();
}
user.updateUsername(username);
user.getLoginInfo(server, userIp, (loginInfo, error) -> {
if (error != null) { if (error != null) {
ErrorUtils.respondInternalError(ctx, error); ErrorUtils.respondInternalError(ctx, error);
return; } else {
} else if (user == null) { APIv3.respondJson(ctx, loginInfo);
user = new User(uuid, username);
user.insert(); // TODO
} }
User finalUser = user;
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
}
finalUser.updateUsername(username, (ignored, error3) -> {
if (error3 != null) {
ErrorUtils.respondInternalError(ctx, error3);
} else {
finalUser.getLoginInfo(server, userIp, (loginInfo, error4) -> {
if (error4 != null) {
ErrorUtils.respondInternalError(ctx, error4);
} else {
APIv3.respondJson(ctx, loginInfo);
}
});
}
});
});
}); });
} }

View File

@ -10,7 +10,7 @@ import net.frozenorb.apiv3.unsorted.Notification;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Date; import java.time.Instant;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -43,14 +43,14 @@ public final class POSTUserRegister implements Handler<RoutingContext> {
return; return;
} }
if (user.getEmailToken() != null && (System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) < TimeUnit.DAYS.toMillis(2)) { if (user.getEmailToken() != null && (System.currentTimeMillis() - user.getEmailTokenSetAt().toEpochMilli()) < TimeUnit.DAYS.toMillis(2)) {
ErrorUtils.respondGeneric(ctx, 200, "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; return;
} }
user.setEmail(email); user.setEmail(email);
user.setEmailToken(new BigInteger(130, new Random()).toString(32)); user.setEmailToken(new BigInteger(130, new Random()).toString(32));
user.setEmailTokenSetAt(new Date()); user.setEmailTokenSetAt(Instant.now());
user.save(); user.save();
Map<String, Object> replacements = ImmutableMap.of( Map<String, Object> replacements = ImmutableMap.of(

View File

@ -7,7 +7,7 @@ import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.TOTPUtils; import net.frozenorb.apiv3.util.TotpUtils;
public final class POSTUserSetupTOTP implements Handler<RoutingContext> { public final class POSTUserSetupTOTP implements Handler<RoutingContext> {
@ -24,13 +24,13 @@ public final class POSTUserSetupTOTP implements Handler<RoutingContext> {
return; return;
} }
GoogleAuthenticatorKey generated = TOTPUtils.generateTOTPSecret(); GoogleAuthenticatorKey generated = TotpUtils.generateTotpSecret();
user.setTotpSecret(generated.getKey()); user.setTotpSecret(generated.getKey());
user.save(); user.save();
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"qrCode", TOTPUtils.getQRCodeURL(user, generated) "qrCode", TotpUtils.getQrCodeUrl(user, generated)
)); ));
} }

View File

@ -8,7 +8,7 @@ import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.IpUtils;
import net.frozenorb.apiv3.util.TOTPUtils; import net.frozenorb.apiv3.util.TotpUtils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -36,7 +36,7 @@ public final class POSTUserVerifyTOTP implements Handler<RoutingContext> {
int providedCode = Integer.parseInt(ctx.request().getParam("code")); int providedCode = Integer.parseInt(ctx.request().getParam("code"));
BlockingCallback<Boolean> recentlyUsedCallback = new BlockingCallback<>(); BlockingCallback<Boolean> recentlyUsedCallback = new BlockingCallback<>();
TOTPUtils.wasRecentlyUsed(user, providedCode, recentlyUsedCallback); TotpUtils.wasRecentlyUsed(user, providedCode, recentlyUsedCallback);
if (recentlyUsedCallback.get()) { if (recentlyUsedCallback.get()) {
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
@ -46,15 +46,15 @@ public final class POSTUserVerifyTOTP implements Handler<RoutingContext> {
return; return;
} }
boolean authorized = TOTPUtils.authorizeUser(user, providedCode); boolean authorized = TotpUtils.authorizeUser(user, providedCode);
if (authorized) { if (authorized) {
BlockingCallback<Void> markPreAuthorizedCallback = new BlockingCallback<>(); BlockingCallback<Void> markPreAuthorizedCallback = new BlockingCallback<>();
TOTPUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS, markPreAuthorizedCallback); TotpUtils.markPreAuthorized(user, userIp, 3, TimeUnit.DAYS, markPreAuthorizedCallback);
markPreAuthorizedCallback.get(); markPreAuthorizedCallback.get();
BlockingCallback<Void> markRecentlyUsedCallback = new BlockingCallback<>(); BlockingCallback<Void> markRecentlyUsedCallback = new BlockingCallback<>();
TOTPUtils.markRecentlyUsed(user, providedCode, markRecentlyUsedCallback); TotpUtils.markRecentlyUsed(user, providedCode, markRecentlyUsedCallback);
markRecentlyUsedCallback.get(); markRecentlyUsedCallback.get();
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(

View File

@ -5,22 +5,22 @@ import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.time.Instant;
public final class DateTypeAdapter extends TypeAdapter<Date> { public final class InstantTypeAdapter extends TypeAdapter<Instant> {
public void write(JsonWriter writer, Date write) throws IOException { public void write(JsonWriter writer, Instant write) throws IOException {
if (write == null) { if (write == null) {
writer.nullValue(); writer.nullValue();
} else { } else {
writer.value(write.getTime()); writer.value(write.toEpochMilli());
} }
} }
// This is used with Gson, which is only used // This is used with Gson, which is only used
// to serialize outgoing responses, thus we // to serialize outgoing responses, thus we
// don't need to have a read method. // don't need to have a read method.
public Date read(JsonReader reader) { public Instant read(JsonReader reader) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }

View File

@ -0,0 +1,18 @@
package net.frozenorb.apiv3.serialization.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.time.Instant;
public final class InstantJsonDeserializer extends JsonDeserializer<Instant> {
@Override
public Instant deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return Instant.ofEpochMilli(jsonParser.getValueAsLong());
}
}

View File

@ -0,0 +1,18 @@
package net.frozenorb.apiv3.serialization.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.Instant;
public final class InstantJsonSerializer extends JsonSerializer<Instant> {
@Override
public void serialize(Instant instant, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeNumber(instant.toEpochMilli());
}
}

View File

@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
public class UUIDJsonDeserializer extends JsonDeserializer<UUID> { public final class UuidJsonDeserializer extends JsonDeserializer<UUID> {
@Override @Override
public UUID deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { public UUID deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {

View File

@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
public final class UUIDJsonSerializer extends JsonSerializer<UUID> { public final class UuidJsonSerializer extends JsonSerializer<UUID> {
@Override @Override
public void serialize(UUID uuid, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { public void serialize(UUID uuid, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {

View File

@ -8,7 +8,7 @@ import org.bson.codecs.EncoderContext;
import java.util.UUID; import java.util.UUID;
public final class UUIDCodec implements Codec<UUID> { public final class UuidCodec implements Codec<UUID> {
@Override @Override
public UUID decode(BsonReader bsonReader, DecoderContext decoderContext) { public UUID decode(BsonReader bsonReader, DecoderContext decoderContext) {

View File

@ -6,11 +6,11 @@ import org.bson.codecs.configuration.CodecRegistry;
import java.util.UUID; import java.util.UUID;
public final class UUIDCodecProvider implements CodecProvider { public final class UuidCodecProvider implements CodecProvider {
public <T> Codec<T> get(Class<T> clazz, CodecRegistry codecRegistry) { public <T> Codec<T> get(Class<T> clazz, CodecRegistry codecRegistry) {
if (clazz == UUID.class) { if (clazz == UUID.class) {
return (Codec<T>) new UUIDCodec(); return (Codec<T>) new UuidCodec();
} else { } else {
return null; return null;
} }

View File

@ -4,7 +4,7 @@ import com.bugsnag.Logger;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class BugsnagSLF4JLogger extends Logger { public class BugsnagSlf4jLogger extends Logger {
public void debug(String message) { public void debug(String message) {
log.debug(message); log.debug(message);

View File

@ -21,6 +21,7 @@ public class ErrorUtils {
} }
public static void respondInternalError(RoutingContext ctx, Throwable error) { public static void respondInternalError(RoutingContext ctx, Throwable error) {
error.printStackTrace();
respondGeneric(ctx, 500, "Internal error: " + error.getClass().getSimpleName()); respondGeneric(ctx, 500, "Internal error: " + error.getClass().getSimpleName());
} }

View File

@ -0,0 +1,37 @@
package net.frozenorb.apiv3.util;
import com.google.common.base.Charsets;
import com.mongodb.async.SingleResultCallback;
import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.maxmind.MaxMindResult;
import org.bson.Document;
import java.util.Base64;
import java.util.Map;
@UtilityClass
public class MaxMindUtils {
private static final String maxMindUserId = APIv3.getConfig().getProperty("maxMind.userId");
private static final String maxMindLicenseKey = APIv3.getConfig().getProperty("maxMind.maxMindLicenseKey");
public static void getInsights(String ip, SingleResultCallback<MaxMindResult> callback) {
// We have to specifically use getHttpSClient(), vertx's http client is dumb.
APIv3.getHttpsClient().get(443, "geoip.maxmind.com", "/geoip/v2.1/insights/" + ip, (response) -> {
response.bodyHandler((body) -> {
Document resJson = Document.parse(body.toString());
callback.onResult(new MaxMindResult(resJson), null);
});
response.exceptionHandler((error) -> {
callback.onResult(null, error);
});
}).putHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString((maxMindUserId + ":" + maxMindLicenseKey).getBytes(Charsets.UTF_8))).end();
}
public static String getEnglishName(Document source) {
return ((Map<String, String>) source.get("names")).get("en");
}
}

View File

@ -12,7 +12,7 @@ import java.util.UUID;
public class MojangUtils { public class MojangUtils {
public static void getName(UUID id, SingleResultCallback<String> callback) { public static void getName(UUID id, SingleResultCallback<String> callback) {
APIv3.getHttpClient().get("sessionserver.mojang.com", "session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> { APIv3.getHttpClient().get("sessionserver.mojang.com", "/session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> {
response.bodyHandler((body) -> { response.bodyHandler((body) -> {
Document resJson = Document.parse(body.toString()); Document resJson = Document.parse(body.toString());
String name = resJson.getString("name"); String name = resJson.getString("name");

View File

@ -12,11 +12,11 @@ import net.frozenorb.apiv3.model.User;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@UtilityClass @UtilityClass
public class TOTPUtils { public class TotpUtils {
private static GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build()); private static GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build());
public static GoogleAuthenticatorKey generateTOTPSecret() { public static GoogleAuthenticatorKey generateTotpSecret() {
return googleAuthenticator.createCredentials(); return googleAuthenticator.createCredentials();
} }
@ -24,7 +24,7 @@ public class TOTPUtils {
return googleAuthenticator.authorize(user.getTotpSecret(), code); return googleAuthenticator.authorize(user.getTotpSecret(), code);
} }
public static String getQRCodeURL(User user, GoogleAuthenticatorKey secret) { public static String getQrCodeUrl(User user, GoogleAuthenticatorKey secret) {
return GoogleAuthenticatorQRGenerator.getOtpAuthURL( return GoogleAuthenticatorQRGenerator.getOtpAuthURL(
"MineHQ Network", "MineHQ Network",
user.getLastUsername(), user.getLastUsername(),

View File

@ -2,7 +2,7 @@ package net.frozenorb.apiv3.util;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import java.util.Date; import java.time.Instant;
@UtilityClass @UtilityClass
public class TimeUtils { public class TimeUtils {
@ -27,8 +27,8 @@ public class TimeUtils {
return (fDays + fHours + fMinutes + fSeconds).trim(); return (fDays + fHours + fMinutes + fSeconds).trim();
} }
public static int getSecondsBetween(Date a, Date b) { public static int getSecondsBetween(Instant a, Instant b) {
return (Math.abs((int) (a.getTime() - b.getTime()) / 1000)); return (Math.abs((int) (a.toEpochMilli() - b.toEpochMilli()) / 1000));
} }
} }

View File

@ -5,9 +5,9 @@ import lombok.experimental.UtilityClass;
import java.util.UUID; import java.util.UUID;
@UtilityClass @UtilityClass
public class UUIDUtils { public class UuidUtils {
public static boolean isAcceptableUUID(UUID uuid) { public static boolean isAcceptableUuid(UUID uuid) {
return uuid.version() == 4; return uuid.version() == 4;
} }