diff --git a/apiv3.properties b/apiv3.properties deleted file mode 100644 index 86d132d..0000000 --- a/apiv3.properties +++ /dev/null @@ -1,18 +0,0 @@ -mongo.address=209.222.96.50 -mongo.port=27017 -mongo.database=MineHQ -mongo.username= -mongo.password= -redis.address=209.222.96.50 -redis.port=6379 -http.port=80 -http.keystoreFile=/home/keystore.idk -http.keystorePassword=123abc -librato.email=cmcdonald.main@gmail.com -librato.apiToken=a818c3eca8a59d6d9cf76dc9f0d237c6aa97f257c482ce3363cf55a5431bc153 -librato.sourceIdentifier=apiv3-dev-01 -mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ -maxMind.userId=66817 -maxMind.maxMindLicenseKey=8Aw9NsOUeOp7 -zang.accountSid=ACf18890845596403e330944d98886440c -zang.authToken=dc70bbd1fbd8411ba133fa93813a461b \ No newline at end of file diff --git a/application.properties b/application.properties new file mode 100644 index 0000000..b952ae8 --- /dev/null +++ b/application.properties @@ -0,0 +1,23 @@ +mongo.address=158.69.26.208 +mongo.port=27027 +mongo.database=MineHQDev +mongo.username= +mongo.password= + +redis.address=209.222.96.50 +redis.port=6379 + +http.port=80 +http.keystoreFile= +http.keystorePassword= + +mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ +mandrill.fromEmail=no-reply@minehq.com +mandrill.fromName=MineHQ Network + +maxMind.userId=66817 +maxMind.licenseKey=8Aw9NsOUeOp7 + +zang.accountSid=ACf18890845596403e330944d98886440c +zang.authToken=dc70bbd1fbd8411ba133fa93813a461b +zang.fromNumber=339-337-5300 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4435ab0..aa7fcd2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,20 +1,37 @@ + 4.0.0 + net.frozenorb APIv3 1.0 - net.frozenorb - minehq-parent - 1.0 + org.springframework.boot + spring-boot-starter-parent + 1.4.2.RELEASE + + http://maven.minehq.com:8081/artifactory minehq-high + UTF-8 + 3.3.3 + ${project.artifactId} + + + . + true + src/main/resources/ + + **/*.jar + + + org.apache.maven.plugins @@ -48,6 +65,21 @@ + + org.apache.maven.plugins + maven-compiler-plugin + 3.5 + + 1.8 + 1.8 + UTF-8 + + + + org.springframework.boot + spring-boot-maven-plugin + 1.4.2.RELEASE + @@ -62,32 +94,44 @@ + + + minehq-repo + ${minehq.mavenUrl}/${minehq.mavenLevel} + + + minehq-repo + ${minehq.mavenUrl}/${minehq.mavenLevel} + + + + + + org.springframework.boot + spring-boot-starter + + io.vertx vertx-core - 3.3.2 + ${vertx.version} io.vertx vertx-web - 3.3.2 + ${vertx.version} io.vertx vertx-redis-client - 3.3.2 + ${vertx.version} io.vertx vertx-circuit-breaker - 3.3.2 - - - io.vertx - vertx-dropwizard-metrics - 3.3.2 + ${vertx.version} @@ -126,13 +170,6 @@ 2.7.0 - - - com.librato.metrics - metrics-librato - 4.1.2.5 - - com.warrenstrange @@ -140,18 +177,6 @@ 1.1.1 - - - org.apache.logging.log4j - log4j-core - 2.6.1 - - - org.slf4j - slf4j-log4j12 - 1.7.21 - - org.projectlombok diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index 78dd416..d1752ab 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -1,27 +1,9 @@ package net.frozenorb.apiv3; -import com.google.common.collect.ImmutableList; import com.google.common.net.MediaType; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.SharedMetricRegistries; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.librato.metrics.LibratoReporter; -import com.mongodb.ConnectionString; -import com.mongodb.MongoCredential; -import com.mongodb.async.client.MongoClient; -import com.mongodb.async.client.MongoClientSettings; -import com.mongodb.async.client.MongoClients; -import com.mongodb.async.client.MongoDatabase; -import com.mongodb.client.model.IndexModel; -import com.mongodb.connection.ClusterSettings; - import net.frozenorb.apiv3.filter.AuthenticationFilter; import net.frozenorb.apiv3.filter.AuthorizationFilter; import net.frozenorb.apiv3.filter.MetricsFilter; @@ -32,7 +14,6 @@ import net.frozenorb.apiv3.model.Rank; import net.frozenorb.apiv3.model.Server; import net.frozenorb.apiv3.model.ServerGroup; import net.frozenorb.apiv3.route.GETDumpsType; -import net.frozenorb.apiv3.route.GETMetrics; import net.frozenorb.apiv3.route.GETSearch; import net.frozenorb.apiv3.route.GETWhoAmI; import net.frozenorb.apiv3.route.POSTLogout; @@ -113,31 +94,19 @@ import net.frozenorb.apiv3.route.users.POSTUsersIdVerifyTotp; import net.frozenorb.apiv3.route.users.POSTUsersUsePasswordResetToken; import net.frozenorb.apiv3.serialization.gson.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.serialization.gson.InstantTypeAdapter; -import net.frozenorb.apiv3.serialization.jackson.InstantJsonDeserializer; -import net.frozenorb.apiv3.serialization.jackson.InstantJsonSerializer; -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.util.EmailUtils; +import net.frozenorb.apiv3.util.SpringUtils; -import org.bson.Document; -import org.bson.codecs.BsonValueCodecProvider; -import org.bson.codecs.DocumentCodecProvider; -import org.bson.codecs.ValueCodecProvider; -import org.bson.codecs.configuration.CodecRegistries; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; import java.time.Instant; -import java.util.List; -import java.util.Properties; -import java.util.UUID; import java.util.concurrent.TimeUnit; -import fr.javatic.mongo.jacksonCodec.JacksonCodecProvider; -import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory; import io.vertx.core.AbstractVerticle; +import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; @@ -150,15 +119,12 @@ import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.LoggerFormat; import io.vertx.ext.web.handler.LoggerHandler; import io.vertx.ext.web.handler.TimeoutHandler; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; @Slf4j -public final class APIv3 extends AbstractVerticle { +@Component +public final class APIv3 extends AbstractVerticle implements ApplicationContextAware { - @Getter private static Vertx vertxInstance; - @Getter private static MongoDatabase database; - @Getter private static final Properties config = new Properties(); private static final Gson gson = new GsonBuilder() .registerTypeAdapter(Instant.class, new InstantTypeAdapter()) .setExclusionStrategies(new FollowAnnotationExclusionStrategy()) @@ -166,299 +132,168 @@ public final class APIv3 extends AbstractVerticle { @Override public void start() { - vertxInstance = vertx; - setupConfig(); - setupDatabase(); - setupMetrics(); setupHttpServer(); - /*User.findAll((users, error) -> { - if (error != null) { - error.printStackTrace(); - return; - } - - for (User user : users) { - String currentUsername = SyncUtils.runBlocking(v -> MojangUtils.getName(user.getId(), v)); - String lastUsername = user.getLastUsername(); - - if (!currentUsername.equals(lastUsername)) { - SyncUtils.runBlocking(v -> user.checkNameCollisions(v)); - } - - log.info(user.getLastUsername() + " - " + user.getLastSeenAt()); - } - });*/ - } - - private void setupConfig() { - try (InputStream in = new FileInputStream("apiv3.properties")) { - config.load(in); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - private void setupDatabase() { - List credentials = ImmutableList.of(); - - if (!config.getProperty("mongo.username").isEmpty()) { - credentials = ImmutableList.of( - MongoCredential.createCredential( - config.getProperty("mongo.username"), - config.getProperty("mongo.database"), - config.getProperty("mongo.password").toCharArray() - ) - ); - } - - ConnectionString connectionString = new ConnectionString("mongodb://" + config.getProperty("mongo.address") + ":" + config.getProperty("mongo.port")); - - MongoClient mongoClient = MongoClients.create(MongoClientSettings - .builder() - .codecRegistry(CodecRegistries.fromProviders(ImmutableList.of( - new UuidCodecProvider(), // MHQ, fixes uuid serialization - new ValueCodecProvider(), - new DocumentCodecProvider(), - new BsonValueCodecProvider(), - new JacksonCodecProvider(createMongoJacksonMapper()) // Jackson codec, provides serialization/deserialization - ))) - .credentialList(credentials) - .clusterSettings(ClusterSettings.builder() - .applyConnectionString(connectionString) - .build() - ) - .build() - ); - - database = mongoClient.getDatabase(config.getProperty("mongo.database")); - database.getCollection("auditLog").createIndexes(ImmutableList.of( - new IndexModel(new Document("user", 1)), - new IndexModel(new Document("performedAt", 1)), - new IndexModel(new Document("type", 1)) - ), (a, b) -> { - }); - database.getCollection("grants").createIndexes(ImmutableList.of( - new IndexModel(new Document("user", 1)), - new IndexModel(new Document("rank", 1)), - new IndexModel(new Document("addedAt", 1)) - ), (a, b) -> { - }); - database.getCollection("ipLog").createIndexes(ImmutableList.of( - new IndexModel(new Document("user", 1)), - new IndexModel(new Document("user", 1).append("userIp", 1)), - new IndexModel(new Document("hashedUserIp", 1)) - ), (a, b) -> { - }); - database.getCollection("ipBans").createIndexes(ImmutableList.of( - new IndexModel(new Document("userIp", 1)) - ), (a, b) -> { - }); - database.getCollection("ipIntel").createIndexes(ImmutableList.of( - new IndexModel(new Document("hashedIp", 1)), - new IndexModel(new Document("location", "2dsphere")) - ), (a, b) -> { - }); - database.getCollection("punishments").createIndexes(ImmutableList.of( - new IndexModel(new Document("user", 1)), - new IndexModel(new Document("type", 1)), - new IndexModel(new Document("addedAt", 1)), - new IndexModel(new Document("addedBy", 1)), - new IndexModel(new Document("linkedIpBanId", 1)) - ), (a, b) -> { - }); - database.getCollection("users").createIndexes(ImmutableList.of( - new IndexModel(new Document("lastUsername", 1)), - new IndexModel(new Document("lastUsernameLower", 1)), - new IndexModel(new Document("emailToken", 1)) - ), (a, b) -> { - }); - database.getCollection("userMeta").createIndexes(ImmutableList.of( - new IndexModel(new Document("user", 1).append("serverGroup", 1)) - ), (a, b) -> { - }); - BannedAsn.updateCache(); BannedCellCarrier.updateCache(); Rank.updateCache(); Server.updateCache(); ServerGroup.updateCache(); - EmailUtils.updateBannedEmailDomains(); - GETDumpsType.updateCache(); } - private ObjectMapper createMongoJacksonMapper() { - ObjectMapper mongoJacksonMapper = ObjectMapperFactory.createObjectMapper(); - SimpleModule module = new SimpleModule(); - - module.addSerializer(Instant.class, new InstantJsonSerializer()); - module.addDeserializer(Instant.class, new InstantJsonDeserializer()); - module.addSerializer(UUID.class, new UuidJsonSerializer()); - module.addDeserializer(UUID.class, new UuidJsonDeserializer()); - - mongoJacksonMapper.registerModule(module); - mongoJacksonMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); - mongoJacksonMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - mongoJacksonMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - - return mongoJacksonMapper; + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + SpringUtils.setBeanFactory(applicationContext); } - private void setupMetrics() { - MetricRegistry registry = SharedMetricRegistries.getOrCreate("apiv3-registry"); - - LibratoReporter.enable( - LibratoReporter.builder( - registry, - config.getProperty("librato.email"), - config.getProperty("librato.apiToken"), - config.getProperty("librato.sourceIdentifier")), - 10, - TimeUnit.SECONDS); + @Bean + public Vertx vertx() { + return vertx; } private void setupHttpServer() { + Environment environment = SpringUtils.getBean(Environment.class); + HttpServerOptions httpServerOptions = new HttpServerOptions(); httpServerOptions.setCompressionSupported(true); - if (!config.getProperty("http.keystoreFile").isEmpty()) { + if (!environment.getProperty("http.keystoreFile").isEmpty()) { httpServerOptions.setSsl(true); httpServerOptions.setKeyStoreOptions( new JksOptions() - .setPassword(config.getProperty("http.keystorePassword")) - .setPath(config.getProperty("http.keystoreFile")) + .setPassword(environment.getProperty("http.keystorePassword")) + .setPath(environment.getProperty("http.keystoreFile")) ); } HttpServer webServer = vertx.createHttpServer(httpServerOptions); - Router http = Router.router(vertx); // we just name this http to make the route declarations easier to read + Router router = Router.router(vertx); - http.route().handler(LoggerHandler.create(LoggerFormat.TINY)); - http.route().handler(TimeoutHandler.create(TimeUnit.SECONDS.toMillis(5))); - http.route().method(HttpMethod.PUT).method(HttpMethod.POST).method(HttpMethod.DELETE).handler(BodyHandler.create()); - http.route().handler(new AuthenticationFilter()); - http.route().handler(new MetricsFilter()); - http.route().handler(new WebsiteUserSessionFilter()); - http.route().handler(new AuthorizationFilter()); - http.exceptionHandler(Throwable::printStackTrace); + router.route().handler(LoggerHandler.create(LoggerFormat.TINY)); + router.route().handler(TimeoutHandler.create(TimeUnit.SECONDS.toMillis(5))); + router.route().method(HttpMethod.PUT).method(HttpMethod.POST).method(HttpMethod.DELETE).handler(BodyHandler.create()); + router.route().handler(SpringUtils.getBean(AuthenticationFilter.class)); + router.route().handler(SpringUtils.getBean(MetricsFilter.class)); + router.route().handler(SpringUtils.getBean(WebsiteUserSessionFilter.class)); + router.route().handler(SpringUtils.getBean(AuthorizationFilter.class)); + router.exceptionHandler(Throwable::printStackTrace); // TODO: The commented out routes - http.get("/accessTokens/:accessToken").blockingHandler(new GETAccessTokensId()); - http.get("/accessTokens").blockingHandler(new GETAccessTokens()); - http.post("/accessTokens").blockingHandler(new POSTAccessTokens(), false); - //http.put("/accessTokens/:accessToken").blockingHandler(new PUTAccessTokensId(), false); - http.delete("/accessTokens/:accessToken").blockingHandler(new DELETEAccessTokensId(), false); + httpGet(router, "/accessTokens/:accessToken", GETAccessTokensId.class); + httpGet(router, "/accessTokens", GETAccessTokens.class); + httpPost(router, "/accessTokens", POSTAccessTokens.class); + //httpPut(router, "/accessTokens/:accessToken", PUTAccessTokensId.class); + httpDelete(router, "/accessTokens/:accessToken", DELETEAccessTokensId.class); - http.get("/auditLog").handler(new GETAuditLog()); - http.post("/auditLog").handler(new POSTAuditLog()); - http.delete("/auditLog/:auditLogEntryId").blockingHandler(new DELETEAuditLogId()); + httpGet(router, "/auditLog", GETAuditLog.class); + httpPost(router, "/auditLog", POSTAuditLog.class); + httpDelete(router, "/auditLog/:auditLogEntryId", DELETEAuditLogId.class); - http.get("/bannedAsns/:bannedAsn").handler(new GETBannedAsnsId()); - http.get("/bannedAsns").handler(new GETBannedAsns()); - http.post("/bannedAsns").blockingHandler(new POSTBannedAsns(), false); - //http.put("/bannedAsns/:bannedAsn").blockingHandler(new PUTBannedAsnsId(), false); - http.delete("/bannedAsns/:bannedAsn").blockingHandler(new DELETEBannedAsnsId(), false); + httpGet(router, "/bannedAsns/:bannedAsn", GETBannedAsnsId.class); + httpGet(router, "/bannedAsns", GETBannedAsns.class); + httpPost(router, "/bannedAsns", POSTBannedAsns.class); + //httpPut(router, "/bannedAsns/:bannedAsn", PUTBannedAsnsId.class); + httpDelete(router, "/bannedAsns/:bannedAsn", DELETEBannedAsnsId.class); - http.get("/bannedCellCarriers/:bannedCellCarrier").handler(new GETBannedCellCarriersId()); - http.get("/bannedCellCarriers").handler(new GETBannedCellCarriers()); - http.post("/bannedCellCarriers").blockingHandler(new POSTBannedCellCarriers(), false); - //http.put("/bannedCellCarriers/:bannedCellCarrier").blockingHandler(new PUTBannedCellCarriersId(), false); - http.delete("/bannedCellCarriers/:bannedCellCarrier").blockingHandler(new DELETEBannedCellCarriersId(), false); + httpGet(router, "/bannedCellCarriers/:bannedCellCarrier", GETBannedCellCarriersId.class); + httpGet(router, "/bannedCellCarriers", GETBannedCellCarriers.class); + httpPost(router, "/bannedCellCarriers", POSTBannedCellCarriers.class); + //httpPut(router, "/bannedCellCarriers/:bannedCellCarrier", PUTBannedCellCarriersId.class); + httpDelete(router, "/bannedCellCarriers/:bannedCellCarrier", DELETEBannedCellCarriersId.class); - http.get("/chatFilter/:chatFilterEntryId").handler(new GETChatFilterId()); - http.get("/chatFilter").handler(new GETChatFilter()); - http.post("/chatFilter").blockingHandler(new POSTChatFilter(), false); - //http.put("/chatFilter/:chatFilterEntryId").blockingHandler(new PUTChatFilterId(), false); - http.delete("/chatFilter/:chatFilterEntryId").blockingHandler(new DELETEChatFilterId(), false); + httpGet(router, "/chatFilter/:chatFilterEntryId", GETChatFilterId.class); + httpGet(router, "/chatFilter", GETChatFilter.class); + httpPost(router, "/chatFilter", POSTChatFilter.class); + //httpPut(router, "/chatFilter/:chatFilterEntryId", PUTChatFilterId.class); + httpDelete(router, "/chatFilter/:chatFilterEntryId", DELETEChatFilterId.class); - http.post("/deployment/updateServer/:serverId").blockingHandler(new POSTDeploymentUpdateServer(), false); + httpPost(router, "/deployment/updateServer/:serverId", POSTDeploymentUpdateServer.class); - http.post("/disposableLoginTokens").blockingHandler(new POSTDisposableLoginTokens(), false); - http.post("/disposableLoginTokens/:disposableLoginToken/use").blockingHandler(new POSTDisposableLoginTokensIdUse(), false); + httpPost(router, "/disposableLoginTokens", POSTDisposableLoginTokens.class); + httpPost(router, "/disposableLoginTokens/:disposableLoginToken/use", POSTDisposableLoginTokensIdUse.class); - http.get("/emailTokens/:emailToken/owner").blockingHandler(new GETEmailTokensIdOwner(), false); - http.post("/emailTokens/:emailToken/confirm").blockingHandler(new POSTEmailTokensIdConfirm(), false); + httpGet(router, "/emailTokens/:emailToken/owner", GETEmailTokensIdOwner.class); + httpPost(router, "/emailTokens/:emailToken/confirm", POSTEmailTokensIdConfirm.class); - http.get("/grants/:grantId").handler(new GETGrantsId()); - http.get("/grants").handler(new GETGrants()); - http.post("/grants").blockingHandler(new POSTGrants(), false); - //http.put("/grants/:grantId").blockingHandler(new PUTGrantsId(), false); - http.delete("/grants/:grantId").blockingHandler(new DELETEGrantsId(), false); + httpGet(router, "/grants/:grantId", GETGrantsId.class); + httpGet(router, "/grants", GETGrants.class); + httpPost(router, "/grants", POSTGrants.class); + //httpPut(router, "/grants/:grantId", PUTGrantsId.class); + httpDelete(router, "/grants/:grantId", DELETEGrantsId.class); - http.get("/ipBans/:ipBanId").handler(new GETIpBansId()); - http.get("/ipBans").handler(new GETIpBans()); - http.post("/ipBans").blockingHandler(new POSTIpBans(), false); - //http.put("/ipBans/:ipBanId").blockingHandler(new PUTIpBansId(), false); - http.delete("/ipBans/:ipBanId").blockingHandler(new DELETEIpBansId(), false); + httpGet(router, "/ipBans/:ipBanId", GETIpBansId.class); + httpGet(router, "/ipBans", GETIpBans.class); + httpPost(router, "/ipBans", POSTIpBans.class); + //httpPut(router, "/ipBans/:ipBanId", PUTIpBansId.class); + httpDelete(router, "/ipBans/:ipBanId", DELETEIpBansId.class); - http.get("/ipIntel/:userIp").handler(new GETIpInteld()); + httpGet(router, "/ipIntel/:userIp", GETIpInteld.class); - http.get("/ipLog/:id").handler(new GETIpLogId()); + httpGet(router, "/ipLog/:id", GETIpLogId.class); - http.post("/lookup/byName").blockingHandler(new POSTLookupByName()); - http.post("/lookup/byUuid").blockingHandler(new POSTLookupByUuid()); + httpPost(router, "/lookup/byName", POSTLookupByName.class); + httpPost(router, "/lookup/byUuid", POSTLookupByUuid.class); - http.get("/notificationTemplates/:notificationTemplateId").handler(new GETNotificationTemplatesId()); - http.get("/notificationTemplates").handler(new GETNotificationTemplates()); - http.post("/notificationTemplates").blockingHandler(new POSTNotificationTemplates(), false); - //http.put("/notificationTemplates/:notificationTemplateId").blockingHandler(new PUTNotificationTemplatesId(), false); - http.delete("/notificationTemplates/:notificationTemplateId").blockingHandler(new DELETENotificationTemplatesId(), false); + httpGet(router, "/notificationTemplates/:notificationTemplateId", GETNotificationTemplatesId.class); + httpGet(router, "/notificationTemplates", GETNotificationTemplates.class); + httpPost(router, "/notificationTemplates", POSTNotificationTemplates.class); + //httpPut(router, "/notificationTemplates/:notificationTemplateId", PUTNotificationTemplatesId.class); + httpDelete(router, "/notificationTemplates/:notificationTemplateId", DELETENotificationTemplatesId.class); - http.get("/phoneIntel/:phone").handler(new GETPhoneInteld()); + httpGet(router, "/phoneIntel/:phone", GETPhoneInteld.class); - http.get("/punishments/:punishmentId").handler(new GETPunishmentsId()); - http.get("/punishments").handler(new GETPunishments()); - http.post("/punishments").blockingHandler(new POSTPunishments(), false); - //http.put("/punishments/:punishmentId").blockingHandler(new PUTPunishmentsId(), false); - http.delete("/punishments/:punishmentId").blockingHandler(new DELETEPunishmentsId(), false); - http.delete("/users/:userId/activePunishment").blockingHandler(new DELETEUsersIdActivePunishment(), false); + httpGet(router, "/punishments/:punishmentId", GETPunishmentsId.class); + httpGet(router, "/punishments", GETPunishments.class); + httpPost(router, "/punishments", POSTPunishments.class); + //httpPut(router, "/punishments/:punishmentId", PUTPunishmentsId.class); + httpDelete(router, "/punishments/:punishmentId", DELETEPunishmentsId.class); + httpDelete(router, "/users/:userId/activePunishment", DELETEUsersIdActivePunishment.class); - http.get("/ranks/:rankId").handler(new GETRanksId()); - http.get("/ranks").handler(new GETRanks()); - http.post("/ranks").blockingHandler(new POSTRanks(), false); - //http.put("/ranks/:rankId").blockingHandler(new PUTRanksId(), false); - http.delete("/ranks/:rankId").blockingHandler(new DELETERanksId(), false); + httpGet(router, "/ranks/:rankId", GETRanksId.class); + httpGet(router, "/ranks", GETRanks.class); + httpPost(router, "/ranks", POSTRanks.class); + //httpPut(router, "/ranks/:rankId", PUTRanksId.class); + httpDelete(router, "/ranks/:rankId", DELETERanksId.class); - http.get("/serverGroups/:serverGroupId").handler(new GETServerGroupsId()); - http.get("/serverGroups").handler(new GETServerGroups()); - http.post("/serverGroups").blockingHandler(new POSTServerGroups(), false); - //http.put("/serverGroups/:serverGroupId").blockingHandler(new PUTServerGroupsId(), false); - http.delete("/serverGroups/:serverGroupId").blockingHandler(new DELETEServerGroupsId(), false); + httpGet(router, "/serverGroups/:serverGroupId", GETServerGroupsId.class); + httpGet(router, "/serverGroups", GETServerGroups.class); + httpPost(router, "/serverGroups", POSTServerGroups.class); + //httpPut(router, "/serverGroups/:serverGroupId", PUTServerGroupsId.class); + httpDelete(router, "/serverGroups/:serverGroupId", DELETEServerGroupsId.class); - http.get("/servers/:serverId").handler(new GETServersId()); - http.get("/servers").handler(new GETServers()); - http.post("/servers/heartbeat").handler(new POSTServersHeartbeat()); - http.post("/servers").blockingHandler(new POSTServers(), false); - //http.put("/servers/:serverId").blockingHandler(new PUTServersId(), false); - http.delete("/servers/:serverId").blockingHandler(new DELETEServersId(), false); + httpGet(router, "/servers/:serverId", GETServersId.class); + httpGet(router, "/servers", GETServers.class); + httpPost(router, "/servers/heartbeat", POSTServersHeartbeat.class); + httpPost(router, "/servers", POSTServers.class); + //httpPut(router, "/servers/:serverId", PUTServersId.class); + httpDelete(router, "/servers/:serverId", DELETEServersId.class); - http.get("/staff").blockingHandler(new GETStaff(), false); - http.get("/users/:userId").handler(new GETUsersId()); - http.get("/users/:userId/compoundedPermissions").handler(new GETUsersIdCompoundedPermissions()); - http.get("/users/:userId/details").blockingHandler(new GETUsersIdDetails(), false); - http.get("/users/:userId/requiresTotp").handler(new GETUsersIdRequiresTotp()); - http.get("/users/:userId/verifyPassword").blockingHandler(new GETUsersIdVerifyPassword(), false); - http.post("/users/:userId/changePassword").blockingHandler(new POSTUsersIdChangePassword(), false); - http.post("/users/:userId/confirmPhone").blockingHandler(new POSTUsersIdConfirmPhone(), false); - http.post("/users/:userId/login").handler(new POSTUsersIdLogin()); - http.post("/users/:userId/notify").blockingHandler(new POSTUsersIdNotify(), false); - http.post("/users/:userId/passwordReset").blockingHandler(new POSTUsersIdPasswordReset(), false); - http.post("/users/:userId/registerEmail").blockingHandler(new POSTUsersIdRegisterEmail(), false); - http.post("/users/:userId/registerPhone").blockingHandler(new POSTUsersIdRegisterPhone(), false); - http.post("/users/usePasswordResetToken").blockingHandler(new POSTUsersUsePasswordResetToken(), false); - http.post("/users/:userId/setupTotp").blockingHandler(new POSTUsersIdSetupTotp(), false); - http.post("/users/:userId/verifyTotp").handler(new POSTUsersIdVerifyTotp()); + httpGet(router, "/staff", GETStaff.class); + httpGet(router, "/users/:userId", GETUsersId.class); + httpGet(router, "/users/:userId/compoundedPermissions", GETUsersIdCompoundedPermissions.class); + httpGet(router, "/users/:userId/details", GETUsersIdDetails.class); + httpGet(router, "/users/:userId/requiresTotp", GETUsersIdRequiresTotp.class); + httpGet(router, "/users/:userId/verifyPassword", GETUsersIdVerifyPassword.class); + httpPost(router, "/users/:userId/changePassword", POSTUsersIdChangePassword.class); + httpPost(router, "/users/:userId/confirmPhone", POSTUsersIdConfirmPhone.class); + httpPost(router, "/users/:userId/login", POSTUsersIdLogin.class); + httpPost(router, "/users/:userId/notify", POSTUsersIdNotify.class); + httpPost(router, "/users/:userId/passwordReset", POSTUsersIdPasswordReset.class); + httpPost(router, "/users/:userId/registerEmail", POSTUsersIdRegisterEmail.class); + httpPost(router, "/users/:userId/registerPhone", POSTUsersIdRegisterPhone.class); + httpPost(router, "/users/usePasswordResetToken", POSTUsersUsePasswordResetToken.class); + httpPost(router, "/users/:userId/setupTotp", POSTUsersIdSetupTotp.class); + httpPost(router, "/users/:userId/verifyTotp", POSTUsersIdVerifyTotp.class); - http.get("/dumps/:dumpType").handler(new GETDumpsType()); - http.get("/metrics").handler(new GETMetrics()); - http.get("/search").blockingHandler(new GETSearch()); - http.get("/whoami").handler(new GETWhoAmI()); - http.post("/logout").handler(new POSTLogout()); + httpGet(router, "/dumps/:dumpType", GETDumpsType.class); + httpGet(router, "/search", GETSearch.class); + httpGet(router, "/whoami", GETWhoAmI.class); + httpPost(router, "/logout", POSTLogout.class); - int port = Integer.parseInt(config.getProperty("http.port")); - webServer.requestHandler(http::accept).listen(port); + int port = environment.getProperty("http.port", Integer.class); + webServer.requestHandler(router::accept).listen(port); } public static void respondJson(RoutingContext ctx, int code, Object response) { @@ -473,4 +308,16 @@ public final class APIv3 extends AbstractVerticle { } } + private void httpGet(Router router, String route, Class handler) { + router.get(route).blockingHandler((Handler) SpringUtils.getBean(handler), false); + } + + private void httpPost(Router router, String route, Class handler) { + router.post(route).blockingHandler((Handler) SpringUtils.getBean(handler), false); + } + + private void httpDelete(Router router, String route, Class handler) { + router.delete(route).blockingHandler((Handler) SpringUtils.getBean(handler), false); + } + } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/Main.java b/src/main/java/net/frozenorb/apiv3/Main.java index c841bfc..2053b4f 100644 --- a/src/main/java/net/frozenorb/apiv3/Main.java +++ b/src/main/java/net/frozenorb/apiv3/Main.java @@ -1,16 +1,28 @@ package net.frozenorb.apiv3; -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import io.vertx.ext.dropwizard.DropwizardMetricsOptions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; -final class Main { +import javax.annotation.PostConstruct; + +import io.vertx.core.Vertx; + +@SpringBootApplication +@EnableScheduling +class Main { + + @Autowired private APIv3 verticle; public static void main(String[] args) { System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory"); - Vertx.vertx(new VertxOptions().setMetricsOptions( - new DropwizardMetricsOptions().setEnabled(true).setRegistryName("apiv3-registry") - )).deployVerticle(new APIv3()); + SpringApplication.run(Main.class, args); + } + + @PostConstruct + public void deployVerticle() { + Vertx.vertx().deployVerticle(verticle); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/config/HttpClientConfig.java b/src/main/java/net/frozenorb/apiv3/config/HttpClientConfig.java new file mode 100644 index 0000000..b2549f5 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/config/HttpClientConfig.java @@ -0,0 +1,23 @@ +package net.frozenorb.apiv3.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; + +@Configuration +public class HttpClientConfig { + + @Bean + public HttpClient httpsClient(Vertx vertx) { + return vertx.createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true)); + } + + @Bean + public HttpClient httpClient(Vertx vertx) { + return vertx.createHttpClient(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/config/MongoConfig.java b/src/main/java/net/frozenorb/apiv3/config/MongoConfig.java new file mode 100644 index 0000000..1109146 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/config/MongoConfig.java @@ -0,0 +1,139 @@ +package net.frozenorb.apiv3.config; + +import com.google.common.collect.ImmutableList; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.mongodb.ConnectionString; +import com.mongodb.MongoCredential; +import com.mongodb.async.client.MongoClient; +import com.mongodb.async.client.MongoClientSettings; +import com.mongodb.async.client.MongoClients; +import com.mongodb.async.client.MongoDatabase; +import com.mongodb.client.model.IndexModel; +import com.mongodb.connection.ClusterSettings; + +import net.frozenorb.apiv3.serialization.jackson.InstantJsonDeserializer; +import net.frozenorb.apiv3.serialization.jackson.InstantJsonSerializer; +import net.frozenorb.apiv3.serialization.jackson.UuidJsonDeserializer; +import net.frozenorb.apiv3.serialization.jackson.UuidJsonSerializer; +import net.frozenorb.apiv3.serialization.mongodb.UuidCodecProvider; + +import org.bson.Document; +import org.bson.codecs.BsonValueCodecProvider; +import org.bson.codecs.DocumentCodecProvider; +import org.bson.codecs.ValueCodecProvider; +import org.bson.codecs.configuration.CodecRegistries; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +import fr.javatic.mongo.jacksonCodec.JacksonCodecProvider; +import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory; + +@Configuration +public class MongoConfig { + + @Bean + public MongoDatabase mongoDatabase( + @Value("${mongo.username}") String username, + @Value("${mongo.database}") String database, + @Value("${mongo.password}") String password, + @Value("${mongo.address}") String address, + @Value("${mongo.port}") int port + ) { + List credentials = ImmutableList.of(); + + if (!username.isEmpty()) { + credentials = ImmutableList.of( + MongoCredential.createCredential(username, database, password.toCharArray()) + ); + } + + ConnectionString connectionString = new ConnectionString("mongodb://" + address + ":" + port); + + MongoClient mongoClient = MongoClients.create(MongoClientSettings + .builder() + .codecRegistry(CodecRegistries.fromProviders(ImmutableList.of( + new UuidCodecProvider(), // MHQ, fixes uuid serialization + new ValueCodecProvider(), + new DocumentCodecProvider(), + new BsonValueCodecProvider(), + new JacksonCodecProvider(createMongoJacksonMapper()) // Jackson codec, provides serialization/deserialization + ))) + .credentialList(credentials) + .clusterSettings(ClusterSettings.builder() + .applyConnectionString(connectionString) + .build() + ) + .build() + ); + + MongoDatabase db = mongoClient.getDatabase(database); + + db.getCollection("auditLog").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("performedAt", 1)), + new IndexModel(new Document("type", 1)) + ), (a, b) -> {}); + db.getCollection("grants").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("rank", 1)), + new IndexModel(new Document("addedAt", 1)) + ), (a, b) -> {}); + db.getCollection("ipLog").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("user", 1).append("userIp", 1)), + new IndexModel(new Document("hashedUserIp", 1)) + ), (a, b) -> {}); + db.getCollection("ipBans").createIndexes(ImmutableList.of( + new IndexModel(new Document("userIp", 1)) + ), (a, b) -> {}); + db.getCollection("ipIntel").createIndexes(ImmutableList.of( + new IndexModel(new Document("hashedIp", 1)), + new IndexModel(new Document("location", "2dsphere")) + ), (a, b) -> {}); + db.getCollection("punishments").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1)), + new IndexModel(new Document("type", 1)), + new IndexModel(new Document("addedAt", 1)), + new IndexModel(new Document("addedBy", 1)), + new IndexModel(new Document("linkedIpBanId", 1)) + ), (a, b) -> {}); + db.getCollection("users").createIndexes(ImmutableList.of( + new IndexModel(new Document("lastUsername", 1)), + new IndexModel(new Document("lastUsernameLower", 1)), + new IndexModel(new Document("emailToken", 1)) + ), (a, b) -> {}); + db.getCollection("userMeta").createIndexes(ImmutableList.of( + new IndexModel(new Document("user", 1).append("serverGroup", 1)) + ), (a, b) -> {}); + + return db; + } + + private ObjectMapper createMongoJacksonMapper() { + ObjectMapper mongoJacksonMapper = ObjectMapperFactory.createObjectMapper(); + SimpleModule module = new SimpleModule(); + + module.addSerializer(Instant.class, new InstantJsonSerializer()); + module.addDeserializer(Instant.class, new InstantJsonDeserializer()); + module.addSerializer(UUID.class, new UuidJsonSerializer()); + module.addDeserializer(UUID.class, new UuidJsonDeserializer()); + + mongoJacksonMapper.registerModule(module); + mongoJacksonMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); + mongoJacksonMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + mongoJacksonMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + return mongoJacksonMapper; + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/config/RedisConfig.java b/src/main/java/net/frozenorb/apiv3/config/RedisConfig.java new file mode 100644 index 0000000..be25b4a --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/config/RedisConfig.java @@ -0,0 +1,27 @@ +package net.frozenorb.apiv3.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.vertx.core.Vertx; +import io.vertx.redis.RedisClient; +import io.vertx.redis.RedisOptions; + +@Configuration +public class RedisConfig { + + @Bean + public RedisClient redisClient(Vertx vertx, RedisOptions redisOptions) { + return RedisClient.create(vertx, redisOptions); + } + + @Bean + public RedisOptions redisOptions( + @Value("${redis.address}") String address, + @Value("${redis.port}") int port + ) { + return new RedisOptions().setAddress(address).setPort(port); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/disposablelogintoken/DisposableLoginTokenService.java b/src/main/java/net/frozenorb/apiv3/disposablelogintoken/DisposableLoginTokenService.java new file mode 100644 index 0000000..6cb3c34 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/disposablelogintoken/DisposableLoginTokenService.java @@ -0,0 +1,18 @@ +package net.frozenorb.apiv3.disposablelogintoken; + +import com.mongodb.async.SingleResultCallback; + +import net.frozenorb.apiv3.model.User; + +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public interface DisposableLoginTokenService { + + void attemptLogin(String token, String userIp, SingleResultCallback callback); + + void createToken(UUID user, String userIp, SingleResultCallback callback); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/disposablelogintoken/RedisDisposableLoginTokenService.java b/src/main/java/net/frozenorb/apiv3/disposablelogintoken/RedisDisposableLoginTokenService.java new file mode 100644 index 0000000..9f7dec1 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/disposablelogintoken/RedisDisposableLoginTokenService.java @@ -0,0 +1,68 @@ +package net.frozenorb.apiv3.disposablelogintoken; + +import com.mongodb.async.SingleResultCallback; + +import net.frozenorb.apiv3.model.User; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import io.vertx.redis.RedisClient; + +@Component +public final class RedisDisposableLoginTokenService implements DisposableLoginTokenService { + + @Autowired private RedisClient redisClient; + + @Override + public void attemptLogin(String token, String userIp, SingleResultCallback callback) { + if (token == null || token.isEmpty()) { + callback.onResult(null, null); + return; + } + + redisClient.get("apiv3:disposableLoginTokens:" + userIp + ":" + token, (result) -> { + if (result.failed()) { + callback.onResult(null, result.cause()); + return; + } + + if (result.result() == null) { + callback.onResult(null, null); + return; + } + + User.findById(result.result(), (user, error) -> { + if (error != null) { + callback.onResult(null, error); + return; + } + + redisClient.del("apiv3:disposableLoginTokens:" + userIp + ":" + token, (result2) -> { + if (result2.failed()) { + callback.onResult(null, result2.cause()); + } else { + callback.onResult(user, null); + } + }); + }); + }); + } + + @Override + public void createToken(UUID user, String userIp, SingleResultCallback callback) { + String token = UUID.randomUUID().toString().replaceAll("-", ""); + + redisClient.setex("apiv3:disposableLoginTokens:" + userIp + ":" + token, TimeUnit.MINUTES.toSeconds(5), user.toString(), (result) -> { + if (result.succeeded()) { + callback.onResult(token, null); + } else { + callback.onResult(null, result.cause()); + } + }); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/email/EmailDomainService.java b/src/main/java/net/frozenorb/apiv3/email/EmailDomainService.java new file mode 100644 index 0000000..e4e8604 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/email/EmailDomainService.java @@ -0,0 +1,10 @@ +package net.frozenorb.apiv3.email; + +import org.springframework.stereotype.Service; + +@Service +public interface EmailDomainService { + + boolean isBannedDomain(String email); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/email/EmailService.java b/src/main/java/net/frozenorb/apiv3/email/EmailService.java new file mode 100644 index 0000000..8626220 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/email/EmailService.java @@ -0,0 +1,14 @@ +package net.frozenorb.apiv3.email; + +import com.mongodb.async.SingleResultCallback; + +import net.frozenorb.apiv3.unsorted.Notification; + +import org.springframework.stereotype.Service; + +@Service +public interface EmailService { + + void sendEmail(Notification notification, String target, SingleResultCallback callback); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/email/GitHubEmailDomainService.java b/src/main/java/net/frozenorb/apiv3/email/GitHubEmailDomainService.java new file mode 100644 index 0000000..e0e4919 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/email/GitHubEmailDomainService.java @@ -0,0 +1,37 @@ +package net.frozenorb.apiv3.email; + +import com.google.common.collect.ImmutableSet; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Set; + +import io.vertx.core.http.HttpClient; + +// no RestTemplate because we don't depend on spring-web yet +@Component +public final class GitHubEmailDomainService implements EmailDomainService { + + @Autowired private HttpClient httpsClient; + private Set bannedDomains = ImmutableSet.of(); + + // 10 minutes, can't use TimeUnit expression in annotation + @Scheduled(fixedRate = 10 * 60 * 1000) + private void updateDomains() { + httpsClient.get(443, "raw.githubusercontent.com", "/martenson/disposable-email-domains/master/disposable_email_blacklist.conf", (response) -> { + response.bodyHandler(body -> bannedDomains = ImmutableSet.copyOf(body.toString().split("\n"))); + response.exceptionHandler(Throwable::printStackTrace); + }).end(); + } + + @Override + public boolean isBannedDomain(String email) { + String[] split = email.split("@"); + String domain = split[1]; + + return bannedDomains.contains(domain.toLowerCase()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/email/MandrillEmailService.java b/src/main/java/net/frozenorb/apiv3/email/MandrillEmailService.java new file mode 100644 index 0000000..8067b27 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/email/MandrillEmailService.java @@ -0,0 +1,64 @@ +package net.frozenorb.apiv3.email; + +import com.google.common.net.MediaType; + +import com.mongodb.async.SingleResultCallback; + +import net.frozenorb.apiv3.unsorted.Notification; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpHeaders; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +@Component +public final class MandrillEmailService implements EmailService { + + @Autowired private HttpClient httpClient; + @Value("${mandrill.apiKey}") String apiKey; + @Value("${mandrill.fromEmail}") String fromEmail; + @Value("${mandrill.fromName}") String fromName; + + @Override + public void sendEmail(Notification notification, String target, SingleResultCallback callback) { + JsonObject requestBody = new JsonObject() + .put("key", apiKey) + .put("message", new JsonObject() + .put("html", notification.getBody()) + .put("subject", notification.getSubject()) + .put("from_email", fromEmail) + .put("from_name", fromName) + .put("to", new JsonArray() + .add(new JsonObject() + .put("email", target) + ) + ) + ); + + httpClient.post("mandrillapp.com", "/api/1.0/messages/send.json", (response) -> { + response.bodyHandler((responseBody) -> { + try { + JsonArray bodyJson = new JsonArray(responseBody.toString()); + JsonObject emailJson = bodyJson.getJsonObject(0); + String emailStatus = emailJson.getString("status"); + + if (emailStatus.equals("rejected") || emailStatus.equals("invalid")) { + callback.onResult(null, new IOException("Illegal email status while reading Mandrill response: " + emailStatus + " (" + emailJson.encode() + ")")); + } else { + callback.onResult(null, null); + } + } catch (Exception ex) { + callback.onResult(null, new IOException("Failed to process Mandrill response: " + responseBody, ex)); + } + }); + response.exceptionHandler((error) -> callback.onResult(null, error)); + }).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()).end(requestBody.encode()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filter/AuthenticationFilter.java b/src/main/java/net/frozenorb/apiv3/filter/AuthenticationFilter.java index 8c581ab..42c73b3 100644 --- a/src/main/java/net/frozenorb/apiv3/filter/AuthenticationFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filter/AuthenticationFilter.java @@ -7,9 +7,12 @@ import net.frozenorb.apiv3.actor.SimpleActor; import net.frozenorb.apiv3.model.AccessToken; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class AuthenticationFilter implements Handler { @Override diff --git a/src/main/java/net/frozenorb/apiv3/filter/AuthorizationFilter.java b/src/main/java/net/frozenorb/apiv3/filter/AuthorizationFilter.java index 031d990..ce080bd 100644 --- a/src/main/java/net/frozenorb/apiv3/filter/AuthorizationFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filter/AuthorizationFilter.java @@ -5,9 +5,12 @@ import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.actor.Actor; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class AuthorizationFilter implements Handler { @Override diff --git a/src/main/java/net/frozenorb/apiv3/filter/MetricsFilter.java b/src/main/java/net/frozenorb/apiv3/filter/MetricsFilter.java index 268e9fa..2ed05c7 100644 --- a/src/main/java/net/frozenorb/apiv3/filter/MetricsFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filter/MetricsFilter.java @@ -1,19 +1,16 @@ package net.frozenorb.apiv3.filter; -import net.frozenorb.apiv3.APIv3; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import io.vertx.redis.RedisClient; -import io.vertx.redis.RedisOptions; +@Component public final class MetricsFilter implements Handler { - private static final RedisClient redisClient = RedisClient.create(APIv3.getVertxInstance(), - new RedisOptions() - .setAddress(APIv3.getConfig().getProperty("redis.address")) - .setPort(Integer.parseInt(APIv3.getConfig().getProperty("redis.port"))) - ); + @Autowired private RedisClient redisClient; @Override public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/filter/WebsiteUserSessionFilter.java b/src/main/java/net/frozenorb/apiv3/filter/WebsiteUserSessionFilter.java index 4e5099d..4f53676 100644 --- a/src/main/java/net/frozenorb/apiv3/filter/WebsiteUserSessionFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filter/WebsiteUserSessionFilter.java @@ -4,9 +4,12 @@ import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.actor.Actor; import net.frozenorb.apiv3.actor.ActorType; +import net.frozenorb.apiv3.usersession.UserSessionService; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; -import net.frozenorb.apiv3.util.UserSessionUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.core.http.HttpMethod; @@ -14,8 +17,11 @@ import io.vertx.ext.web.RoutingContext; import lombok.extern.slf4j.Slf4j; @Slf4j +@Component public final class WebsiteUserSessionFilter implements Handler { + @Autowired private UserSessionService userSessionService; + @Override public void handle(RoutingContext ctx) { Actor actor = ctx.get("actor"); @@ -38,7 +44,7 @@ public final class WebsiteUserSessionFilter implements Handler { return; } - UserSessionUtils.sessionExists(userIp, userSession, (exists, error) -> { + userSessionService.sessionExists(userIp, userSession, (exists, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); return; @@ -78,7 +84,6 @@ public final class WebsiteUserSessionFilter implements Handler { /*if (method == HttpMethod.GET) { switch (path) { case "/ranks": - case "/metrics": case "/staff": case "/servers": case "/servergroups": diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindCity.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpCity.java similarity index 52% rename from src/main/java/net/frozenorb/apiv3/maxmind/MaxMindCity.java rename to src/main/java/net/frozenorb/apiv3/geoip/GeoIpCity.java index e768e98..f34aff8 100644 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindCity.java +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpCity.java @@ -1,22 +1,20 @@ -package net.frozenorb.apiv3.maxmind; - -import net.frozenorb.apiv3.util.MaxMindUtils; +package net.frozenorb.apiv3.geoip; import io.vertx.core.json.JsonObject; import lombok.Getter; -public final class MaxMindCity { +public final class GeoIpCity { @Getter private int confidence; @Getter private int geonameId; @Getter private String name; - private MaxMindCity() {} // For Jackson + private GeoIpCity() {} // For Jackson - public MaxMindCity(JsonObject legacy) { + public GeoIpCity(JsonObject legacy) { this.confidence = legacy.getInteger("confidence", -1); this.geonameId = legacy.getInteger("geoname_id", -1); - this.name = MaxMindUtils.getEnglishName(legacy); + this.name = legacy.getJsonObject("names", new JsonObject()).getString("en", "INVALID"); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/geoip/GeoIpContinent.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpContinent.java new file mode 100644 index 0000000..5fecde0 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpContinent.java @@ -0,0 +1,20 @@ +package net.frozenorb.apiv3.geoip; + +import io.vertx.core.json.JsonObject; +import lombok.Getter; + +public final class GeoIpContinent { + + @Getter private String code; + @Getter private int geonameId; + @Getter private String name; + + private GeoIpContinent() {} // For Jackson + + public GeoIpContinent(JsonObject legacy) { + this.code = legacy.getString("code", ""); + this.geonameId = legacy.getInteger("geoname_id", -1); + this.name = legacy.getJsonObject("names", new JsonObject()).getString("en", "INVALID"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindCountry.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpCountry.java similarity index 58% rename from src/main/java/net/frozenorb/apiv3/maxmind/MaxMindCountry.java rename to src/main/java/net/frozenorb/apiv3/geoip/GeoIpCountry.java index 68db137..c51c6fe 100644 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindCountry.java +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpCountry.java @@ -1,24 +1,22 @@ -package net.frozenorb.apiv3.maxmind; - -import net.frozenorb.apiv3.util.MaxMindUtils; +package net.frozenorb.apiv3.geoip; import io.vertx.core.json.JsonObject; import lombok.Getter; -public final class MaxMindCountry { +public final class GeoIpCountry { @Getter private String isoCode; @Getter private int confidence; @Getter private int geonameId; @Getter private String name; - private MaxMindCountry() {} // For Jackson + private GeoIpCountry() {} // For Jackson - public MaxMindCountry(JsonObject legacy) { + public GeoIpCountry(JsonObject legacy) { this.isoCode = legacy.getString("iso_code", ""); this.confidence = legacy.getInteger("confidence", -1); this.geonameId = legacy.getInteger("geoname_id", -1); - this.name = MaxMindUtils.getEnglishName(legacy); + this.name = legacy.getJsonObject("names", new JsonObject()).getString("en", "INVALID"); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/geoip/GeoIpInfo.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpInfo.java new file mode 100644 index 0000000..fe11ef1 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpInfo.java @@ -0,0 +1,43 @@ +package net.frozenorb.apiv3.geoip; + +import com.google.common.collect.ImmutableList; + +import java.util.LinkedList; +import java.util.List; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import lombok.Getter; + +public final class GeoIpInfo { + + @Getter private GeoIpContinent continent; + @Getter private GeoIpCity city; + @Getter private GeoIpPostal postal; + @Getter private GeoIpTraits traits; + @Getter private GeoIpLocation location; + @Getter private List subdivisions; + @Getter private GeoIpCountry country; + @Getter private GeoIpRegisteredCountry registeredCountry; + + private GeoIpInfo() {} // For Jackson + + public GeoIpInfo(JsonObject legacy) { + this.continent = new GeoIpContinent(legacy.getJsonObject("continent", new JsonObject())); + this.city = new GeoIpCity(legacy.getJsonObject("city", new JsonObject())); + this.postal = new GeoIpPostal(legacy.getJsonObject("postal", new JsonObject())); + this.traits = new GeoIpTraits(legacy.getJsonObject("traits")); + this.location = new GeoIpLocation(legacy.getJsonObject("location", new JsonObject())); + this.country = new GeoIpCountry(legacy.getJsonObject("country", new JsonObject())); + this.registeredCountry = new GeoIpRegisteredCountry(legacy.getJsonObject("registered_country", new JsonObject())); + + List subdivisions = new LinkedList<>(); + + for (Object subdivision : legacy.getJsonArray("subdivisions", new JsonArray())) { + subdivisions.add(new GeoIpSubdivision((JsonObject) subdivision)); + } + + this.subdivisions = ImmutableList.copyOf(subdivisions); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindLocation.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpLocation.java similarity index 81% rename from src/main/java/net/frozenorb/apiv3/maxmind/MaxMindLocation.java rename to src/main/java/net/frozenorb/apiv3/geoip/GeoIpLocation.java index f0652ad..dbc395a 100644 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindLocation.java +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpLocation.java @@ -1,9 +1,9 @@ -package net.frozenorb.apiv3.maxmind; +package net.frozenorb.apiv3.geoip; import io.vertx.core.json.JsonObject; import lombok.Getter; -public final class MaxMindLocation { +public final class GeoIpLocation { @Getter private double latitude; @Getter private double longitude; @@ -13,9 +13,9 @@ public final class MaxMindLocation { @Getter private int metroCode; @Getter private int averageIncome; - private MaxMindLocation() {} // For Jackson + private GeoIpLocation() {} // For Jackson - public MaxMindLocation(JsonObject legacy) { + public GeoIpLocation(JsonObject legacy) { this.latitude = legacy.getDouble("latitude", -1D); this.longitude = legacy.getDouble("longitude", -1D); this.accuracyRadius = legacy.getInteger("accuracy_radius", -1); diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindPostal.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpPostal.java similarity index 59% rename from src/main/java/net/frozenorb/apiv3/maxmind/MaxMindPostal.java rename to src/main/java/net/frozenorb/apiv3/geoip/GeoIpPostal.java index 1577ff2..a358745 100644 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindPostal.java +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpPostal.java @@ -1,16 +1,16 @@ -package net.frozenorb.apiv3.maxmind; +package net.frozenorb.apiv3.geoip; import io.vertx.core.json.JsonObject; import lombok.Getter; -public final class MaxMindPostal { +public final class GeoIpPostal { @Getter private String code; @Getter private int confidence; - private MaxMindPostal() {} // For Jackson + private GeoIpPostal() {} // For Jackson - public MaxMindPostal(JsonObject legacy) { + public GeoIpPostal(JsonObject legacy) { this.code = legacy.getString("code", ""); this.confidence = legacy.getInteger("confidence", -1); } diff --git a/src/main/java/net/frozenorb/apiv3/geoip/GeoIpRegisteredCountry.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpRegisteredCountry.java new file mode 100644 index 0000000..7d701d0 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpRegisteredCountry.java @@ -0,0 +1,20 @@ +package net.frozenorb.apiv3.geoip; + +import io.vertx.core.json.JsonObject; +import lombok.Getter; + +public final class GeoIpRegisteredCountry { + + @Getter private String isoCode; + @Getter private int geonameId; + @Getter private String name; + + private GeoIpRegisteredCountry() {} // For Jackson + + public GeoIpRegisteredCountry(JsonObject legacy) { + this.isoCode = legacy.getString("iso_code", ""); + this.geonameId = legacy.getInteger("geoname_id", -1); + this.name = legacy.getJsonObject("names", new JsonObject()).getString("en", "INVALID"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/geoip/GeoIpService.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpService.java new file mode 100644 index 0000000..6aecc02 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpService.java @@ -0,0 +1,12 @@ +package net.frozenorb.apiv3.geoip; + +import com.mongodb.async.SingleResultCallback; + +import org.springframework.stereotype.Service; + +@Service +public interface GeoIpService { + + void lookupInfo(String ip, SingleResultCallback callback); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindSubdivision.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpSubdivision.java similarity index 58% rename from src/main/java/net/frozenorb/apiv3/maxmind/MaxMindSubdivision.java rename to src/main/java/net/frozenorb/apiv3/geoip/GeoIpSubdivision.java index 6ad6b78..f5c0c58 100644 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindSubdivision.java +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpSubdivision.java @@ -1,24 +1,22 @@ -package net.frozenorb.apiv3.maxmind; - -import net.frozenorb.apiv3.util.MaxMindUtils; +package net.frozenorb.apiv3.geoip; import io.vertx.core.json.JsonObject; import lombok.Getter; -public final class MaxMindSubdivision { +public final class GeoIpSubdivision { @Getter private String isoCode; @Getter private int confidence; @Getter private int geonameId; @Getter private String name; - private MaxMindSubdivision() {} // For Jackson + private GeoIpSubdivision() {} // For Jackson - public MaxMindSubdivision(JsonObject legacy) { + public GeoIpSubdivision(JsonObject legacy) { this.isoCode = legacy.getString("iso_code", ""); this.confidence = legacy.getInteger("confidence", -1); this.geonameId = legacy.getInteger("geoname_id", -1); - this.name = MaxMindUtils.getEnglishName(legacy); + this.name = legacy.getJsonObject("names", new JsonObject()).getString("en", "INVALID"); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindTraits.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpTraits.java similarity index 60% rename from src/main/java/net/frozenorb/apiv3/maxmind/MaxMindTraits.java rename to src/main/java/net/frozenorb/apiv3/geoip/GeoIpTraits.java index 3ade123..f01205a 100644 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindTraits.java +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpTraits.java @@ -1,25 +1,25 @@ -package net.frozenorb.apiv3.maxmind; +package net.frozenorb.apiv3.geoip; import io.vertx.core.json.JsonObject; import lombok.Getter; -public final class MaxMindTraits { +public final class GeoIpTraits { @Getter private String isp; @Getter private String domain; @Getter private int asn; @Getter private String asnOrganization; - @Getter private MaxMindUserType userType; + @Getter private GeoIpUserType userType; @Getter private String organization; - private MaxMindTraits() {} // For Jackson + private GeoIpTraits() {} // For Jackson - public MaxMindTraits(JsonObject legacy) { + public GeoIpTraits(JsonObject legacy) { this.isp = legacy.getString("isp", ""); this.domain = legacy.getString("domain", ""); this.asn = legacy.getInteger("autonomous_system_number", -1); this.asnOrganization = legacy.getString("autonomous_system_organization", ""); - this.userType = legacy.containsKey("user_type") ? MaxMindUserType.valueOf(legacy.getString("user_type").toUpperCase()) : MaxMindUserType.UNKNOWN; + this.userType = legacy.containsKey("user_type") ? GeoIpUserType.valueOf(legacy.getString("user_type").toUpperCase()) : GeoIpUserType.UNKNOWN; this.organization = legacy.getString("organization", ""); } diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindUserType.java b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpUserType.java similarity index 78% rename from src/main/java/net/frozenorb/apiv3/maxmind/MaxMindUserType.java rename to src/main/java/net/frozenorb/apiv3/geoip/GeoIpUserType.java index d8d7645..53afe06 100644 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindUserType.java +++ b/src/main/java/net/frozenorb/apiv3/geoip/GeoIpUserType.java @@ -1,8 +1,8 @@ -package net.frozenorb.apiv3.maxmind; +package net.frozenorb.apiv3.geoip; import lombok.Getter; -public enum MaxMindUserType { +public enum GeoIpUserType { BUSINESS(true), CAFE(true), @@ -23,7 +23,7 @@ public enum MaxMindUserType { @Getter private final boolean allowed; - MaxMindUserType(boolean allowed) { + GeoIpUserType(boolean allowed) { this.allowed = allowed; } diff --git a/src/main/java/net/frozenorb/apiv3/geoip/MaxMindGeoIpService.java b/src/main/java/net/frozenorb/apiv3/geoip/MaxMindGeoIpService.java new file mode 100644 index 0000000..10c88ff --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/geoip/MaxMindGeoIpService.java @@ -0,0 +1,81 @@ +package net.frozenorb.apiv3.geoip; + +import com.google.common.base.Charsets; + +import com.mongodb.async.SingleResultCallback; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Base64; + +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.json.JsonObject; + +@Component +public final class MaxMindGeoIpService implements GeoIpService { + + @Autowired private HttpClient httpsClient; + @Value("${maxMind.userId}") private String userId; + @Value("${maxMind.licenseKey}") private String licenseKey; + private final CircuitBreaker breaker; + + // MaxMind likes to randomly not respond, so we take advantage of the circuit breaker pattern to only + // check MaxMind periodically (while it's in a non-responsive state) to keep our average response times + // nice and low. + @Autowired + public MaxMindGeoIpService(Vertx vertx) { + this.breaker = CircuitBreaker.create(getClass().getName(), vertx, + new CircuitBreakerOptions() + .setMaxFailures(5) + .setTimeout(5000) // 5 seconds + .setFallbackOnFailure(true) + .setResetTimeout(120_000) // 2 minutes + ); + } + + @Override + public void lookupInfo(String ip, SingleResultCallback callback) { + breaker.execute((future) -> { + String authHeader = "Basic " + Base64.getEncoder().encodeToString((userId + ":" + licenseKey).getBytes(Charsets.UTF_8)); + + httpsClient.get(443, "geoip.maxmind.com", "/geoip/v2.1/insights/" + ip, (response) -> { + response.bodyHandler((body) -> { + JsonObject bodyJson = new JsonObject(body.toString()); + + try { + GeoIpInfo geoIpInfo = new GeoIpInfo(bodyJson); + + // we have to check !isComplete() because the circuit breaker's timeout might mark us as failed already + if (!future.isComplete()) { + future.complete(geoIpInfo); + } + } catch (Exception ignored) { + // we have to check !isComplete() because the circuit breaker's timeout might mark us as failed already + if (!future.isComplete()) { + future.complete(null); + } + } + }); + + response.exceptionHandler((error) -> { + // we have to check !isComplete() because the circuit breaker's timeout will might us as failed already + if (!future.isComplete()) { + future.fail(error); + } + }); + }).putHeader("Authorization", authHeader).end(); + }).setHandler((result) -> { + if (result.failed()) { + callback.onResult(null, result.cause()); + } else { + callback.onResult((GeoIpInfo) result.result(), null); + } + }); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindContinent.java b/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindContinent.java deleted file mode 100644 index 9949c22..0000000 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindContinent.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.frozenorb.apiv3.maxmind; - -import net.frozenorb.apiv3.util.MaxMindUtils; - -import io.vertx.core.json.JsonObject; -import lombok.Getter; - -public final class MaxMindContinent { - - @Getter private String code; - @Getter private int geonameId; - @Getter private String name; - - private MaxMindContinent() {} // For Jackson - - public MaxMindContinent(JsonObject legacy) { - this.code = legacy.getString("code", ""); - this.geonameId = legacy.getInteger("geoname_id", -1); - this.name = MaxMindUtils.getEnglishName(legacy); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindRegisteredCountry.java b/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindRegisteredCountry.java deleted file mode 100644 index 958b112..0000000 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindRegisteredCountry.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.frozenorb.apiv3.maxmind; - -import net.frozenorb.apiv3.util.MaxMindUtils; - -import io.vertx.core.json.JsonObject; -import lombok.Getter; - -public final class MaxMindRegisteredCountry { - - @Getter private String isoCode; - @Getter private int geonameId; - @Getter private String name; - - private MaxMindRegisteredCountry() {} // For Jackson - - public MaxMindRegisteredCountry(JsonObject legacy) { - this.isoCode = legacy.getString("iso_code", ""); - this.geonameId = legacy.getInteger("geoname_id", -1); - this.name = MaxMindUtils.getEnglishName(legacy); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindResult.java b/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindResult.java deleted file mode 100644 index e5f34ec..0000000 --- a/src/main/java/net/frozenorb/apiv3/maxmind/MaxMindResult.java +++ /dev/null @@ -1,43 +0,0 @@ -package net.frozenorb.apiv3.maxmind; - -import com.google.common.collect.ImmutableList; - -import java.util.LinkedList; -import java.util.List; - -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import lombok.Getter; - -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 List subdivisions; - @Getter private MaxMindCountry country; - @Getter private MaxMindRegisteredCountry registeredCountry; - - private MaxMindResult() {} // For Jackson - - public MaxMindResult(JsonObject legacy) { - this.continent = new MaxMindContinent(legacy.getJsonObject("continent", new JsonObject())); - this.city = new MaxMindCity(legacy.getJsonObject("city", new JsonObject())); - this.postal = new MaxMindPostal(legacy.getJsonObject("postal", new JsonObject())); - this.traits = new MaxMindTraits(legacy.getJsonObject("traits")); - this.location = new MaxMindLocation(legacy.getJsonObject("location", new JsonObject())); - this.country = new MaxMindCountry(legacy.getJsonObject("country", new JsonObject())); - this.registeredCountry = new MaxMindRegisteredCountry(legacy.getJsonObject("registered_country", new JsonObject())); - - List subdivisions = new LinkedList<>(); - - for (Object subdivision : legacy.getJsonArray("subdivisions", new JsonArray())) { - subdivisions.add(new MaxMindSubdivision((JsonObject) subdivision)); - } - - this.subdivisions = ImmutableList.copyOf(subdivisions); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/model/AccessToken.java b/src/main/java/net/frozenorb/apiv3/model/AccessToken.java index 1539591..f8e4105 100644 --- a/src/main/java/net/frozenorb/apiv3/model/AccessToken.java +++ b/src/main/java/net/frozenorb/apiv3/model/AccessToken.java @@ -4,10 +4,11 @@ import com.google.common.collect.ImmutableList; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actor.ActorType; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -27,7 +28,7 @@ import lombok.Setter; @AllArgsConstructor public final class AccessToken { - private static final MongoCollection accessTokensCollection = APIv3.getDatabase().getCollection("accessTokens", AccessToken.class); + private static final MongoCollection accessTokensCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("accessTokens", AccessToken.class); @Getter @Id private String id; @Getter private String actorName; diff --git a/src/main/java/net/frozenorb/apiv3/model/AuditLogEntry.java b/src/main/java/net/frozenorb/apiv3/model/AuditLogEntry.java index 2c76c25..b93635f 100644 --- a/src/main/java/net/frozenorb/apiv3/model/AuditLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/model/AuditLogEntry.java @@ -4,11 +4,12 @@ import com.google.common.collect.ImmutableMap; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actor.Actor; import net.frozenorb.apiv3.actor.ActorType; import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -27,7 +28,7 @@ import lombok.Getter; @Entity public final class AuditLogEntry { - private static final MongoCollection auditLogCollection = APIv3.getDatabase().getCollection("auditLog", AuditLogEntry.class); + private static final MongoCollection auditLogCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("auditLog", AuditLogEntry.class); @Getter @Id private String id; @Getter private UUID user; diff --git a/src/main/java/net/frozenorb/apiv3/model/BannedAsn.java b/src/main/java/net/frozenorb/apiv3/model/BannedAsn.java index d132a62..197474a 100644 --- a/src/main/java/net/frozenorb/apiv3/model/BannedAsn.java +++ b/src/main/java/net/frozenorb/apiv3/model/BannedAsn.java @@ -4,9 +4,10 @@ import com.google.common.collect.ImmutableList; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -20,13 +21,14 @@ import java.util.concurrent.TimeUnit; import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; +import io.vertx.core.Vertx; import lombok.Getter; import lombok.Setter; @Entity public final class BannedAsn { - private static final MongoCollection bannedAsnsCollection = APIv3.getDatabase().getCollection("bannedAsns", BannedAsn.class); + private static final MongoCollection bannedAsnsCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("bannedAsns", BannedAsn.class); private static Map bannedAsnIdCache = null; private static List bannedAsnCache = null; @@ -45,7 +47,7 @@ public final class BannedAsn { } static { - APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateCache()); + SpringUtils.getBean(Vertx.class).setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateCache()); } public static void updateCache() { diff --git a/src/main/java/net/frozenorb/apiv3/model/BannedCellCarrier.java b/src/main/java/net/frozenorb/apiv3/model/BannedCellCarrier.java index dfb0654..1b50584 100644 --- a/src/main/java/net/frozenorb/apiv3/model/BannedCellCarrier.java +++ b/src/main/java/net/frozenorb/apiv3/model/BannedCellCarrier.java @@ -5,9 +5,10 @@ import com.google.common.collect.ImmutableList; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -21,13 +22,14 @@ import java.util.concurrent.TimeUnit; import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; +import io.vertx.core.Vertx; import lombok.Getter; import lombok.Setter; @Entity public final class BannedCellCarrier { - private static final MongoCollection bannedCellCarriersCollection = APIv3.getDatabase().getCollection("bannedCellCarriers", BannedCellCarrier.class); + private static final MongoCollection bannedCellCarriersCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("bannedCellCarriers", BannedCellCarrier.class); private static Map bannedCellCarrierIdCache = null; private static List bannedCellCarrierCache = null; @@ -46,7 +48,7 @@ public final class BannedCellCarrier { } static { - APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateCache()); + SpringUtils.getBean(Vertx.class).setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateCache()); } public static void updateCache() { diff --git a/src/main/java/net/frozenorb/apiv3/model/ChatFilterEntry.java b/src/main/java/net/frozenorb/apiv3/model/ChatFilterEntry.java index b269473..788db7c 100644 --- a/src/main/java/net/frozenorb/apiv3/model/ChatFilterEntry.java +++ b/src/main/java/net/frozenorb/apiv3/model/ChatFilterEntry.java @@ -2,9 +2,10 @@ package net.frozenorb.apiv3.model; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -19,7 +20,7 @@ import lombok.Getter; @Entity public final class ChatFilterEntry { - private static final MongoCollection chatFilterCollection = APIv3.getDatabase().getCollection("chatFilter", ChatFilterEntry.class); + private static final MongoCollection chatFilterCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("chatFilter", ChatFilterEntry.class); @Getter @Id private String id; @Getter private String regex; diff --git a/src/main/java/net/frozenorb/apiv3/model/Grant.java b/src/main/java/net/frozenorb/apiv3/model/Grant.java index 6e9d198..5d77f49 100644 --- a/src/main/java/net/frozenorb/apiv3/model/Grant.java +++ b/src/main/java/net/frozenorb/apiv3/model/Grant.java @@ -4,9 +4,10 @@ import com.google.common.collect.Collections2; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -32,7 +33,7 @@ import lombok.Getter; @AllArgsConstructor public final class Grant { - private static final MongoCollection grantsCollection = APIv3.getDatabase().getCollection("grants", Grant.class); + private static final MongoCollection grantsCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("grants", Grant.class); @Getter @Id private String id; @Getter private UUID user; diff --git a/src/main/java/net/frozenorb/apiv3/model/IpBan.java b/src/main/java/net/frozenorb/apiv3/model/IpBan.java index 4377aef..7abf313 100644 --- a/src/main/java/net/frozenorb/apiv3/model/IpBan.java +++ b/src/main/java/net/frozenorb/apiv3/model/IpBan.java @@ -2,11 +2,12 @@ package net.frozenorb.apiv3.model; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actor.Actor; import net.frozenorb.apiv3.actor.ActorType; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.TimeUtils; @@ -29,7 +30,7 @@ import lombok.Getter; @AllArgsConstructor public final class IpBan { - private static final MongoCollection ipBansCollection = APIv3.getDatabase().getCollection("ipBans", IpBan.class); + private static final MongoCollection ipBansCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("ipBans", IpBan.class); @Getter @Id private String id; @Getter private String userIp; diff --git a/src/main/java/net/frozenorb/apiv3/model/IpIntel.java b/src/main/java/net/frozenorb/apiv3/model/IpIntel.java index 3e0e0fe..9ff7957 100644 --- a/src/main/java/net/frozenorb/apiv3/model/IpIntel.java +++ b/src/main/java/net/frozenorb/apiv3/model/IpIntel.java @@ -5,14 +5,16 @@ import com.google.common.hash.Hashing; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.maxmind.MaxMindResult; +import net.frozenorb.apiv3.geoip.GeoIpInfo; +import net.frozenorb.apiv3.geoip.GeoIpService; import net.frozenorb.apiv3.util.GeoJsonPoint; -import net.frozenorb.apiv3.util.MaxMindUtils; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; +import org.springframework.core.env.Environment; import java.time.Instant; import java.util.ArrayList; @@ -34,12 +36,12 @@ import lombok.Setter; @AllArgsConstructor public final class IpIntel { - private static final MongoCollection ipIntelCollection = APIv3.getDatabase().getCollection("ipIntel", IpIntel.class); + private static final MongoCollection ipIntelCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("ipIntel", IpIntel.class); @Getter @Setter @Id private String id; @Getter private String hashedIp; @Getter private Instant lastUpdatedAt; - @Getter private MaxMindResult result; + @Getter private GeoIpInfo result; @Getter private GeoJsonPoint location; public static void findAllNoSort(SingleResultCallback> callback) { @@ -65,11 +67,11 @@ public final class IpIntel { } else if (existingIpIntel != null) { callback.onResult(existingIpIntel, null); } else { - MaxMindUtils.getInsights(id, (maxMindResult, error2) -> { + SpringUtils.getBean(GeoIpService.class).lookupInfo(id, (geoIpResult, error2) -> { if (error2 != null) { callback.onResult(null, error2); - } else if (maxMindResult != null) { - IpIntel newIpIntel = new IpIntel(id, maxMindResult); + } else if (geoIpResult != null) { + IpIntel newIpIntel = new IpIntel(id, geoIpResult); ipIntelCollection.insertOne(newIpIntel, SyncUtils.vertxWrap((ignored, error3) -> { if (error3 != null) { @@ -110,19 +112,19 @@ public final class IpIntel { Future createNewIntelFuture = Future.future(); createNewIntelFutures.add(createNewIntelFuture); - MaxMindUtils.getInsights(ip, (maxMindResult, error2) -> { + SpringUtils.getBean(GeoIpService.class).lookupInfo(ip, (geoIpResult, error2) -> { if (error2 != null) { createNewIntelFuture.fail(error2); return; } // MaxMind failed to return result - if (maxMindResult == null) { + if (geoIpResult == null) { createNewIntelFuture.complete(); return; } - IpIntel newIpIntel = new IpIntel(ip, maxMindResult); + IpIntel newIpIntel = new IpIntel(ip, geoIpResult); ipIntelCollection.insertOne(newIpIntel, SyncUtils.vertxWrap((ignored, error3) -> { if (error3 != null) { @@ -147,9 +149,9 @@ public final class IpIntel { private IpIntel() {} // For Jackson - private IpIntel(String ip, MaxMindResult result) { + private IpIntel(String ip, GeoIpInfo result) { this.id = ip; - this.hashedIp = Hashing.sha256().hashString(id + APIv3.getConfig().getProperty("ipHashing.salt"), Charsets.UTF_8).toString(); + this.hashedIp = Hashing.sha256().hashString(id + SpringUtils.getBean(Environment.class).getProperty("ipHashing.salt"), Charsets.UTF_8).toString(); this.lastUpdatedAt = Instant.now(); this.result = result; this.location = new GeoJsonPoint(result.getLocation()); diff --git a/src/main/java/net/frozenorb/apiv3/model/IpLogEntry.java b/src/main/java/net/frozenorb/apiv3/model/IpLogEntry.java index d19a8ce..05ea179 100644 --- a/src/main/java/net/frozenorb/apiv3/model/IpLogEntry.java +++ b/src/main/java/net/frozenorb/apiv3/model/IpLogEntry.java @@ -5,13 +5,15 @@ import com.google.common.hash.Hashing; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; import org.bson.types.ObjectId; +import org.springframework.core.env.Environment; import java.time.Instant; import java.util.LinkedList; @@ -27,7 +29,7 @@ import lombok.Getter; @AllArgsConstructor public final class IpLogEntry { - private static final MongoCollection ipLogCollection = APIv3.getDatabase().getCollection("ipLog", IpLogEntry.class); + private static final MongoCollection ipLogCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("ipLog", IpLogEntry.class); @Getter @Id private String id; @Getter private UUID user; @@ -79,7 +81,7 @@ public final class IpLogEntry { this.id = new ObjectId().toString(); this.user = user.getId(); this.userIp = userIp; - this.hashedUserIp = Hashing.sha256().hashString(userIp + APIv3.getConfig().getProperty("ipHashing.salt"), Charsets.UTF_8).toString(); + this.hashedUserIp = Hashing.sha256().hashString(userIp + SpringUtils.getBean(Environment.class).getProperty("ipHashing.salt"), Charsets.UTF_8).toString(); this.firstSeenAt = Instant.now(); this.lastSeenAt = Instant.now(); this.uses = 0; diff --git a/src/main/java/net/frozenorb/apiv3/model/NotificationTemplate.java b/src/main/java/net/frozenorb/apiv3/model/NotificationTemplate.java index 260d1c0..21be10e 100644 --- a/src/main/java/net/frozenorb/apiv3/model/NotificationTemplate.java +++ b/src/main/java/net/frozenorb/apiv3/model/NotificationTemplate.java @@ -2,9 +2,10 @@ package net.frozenorb.apiv3.model; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -21,7 +22,7 @@ import lombok.Setter; @Entity public final class NotificationTemplate { - private static final MongoCollection notificationTemplatesCollection = APIv3.getDatabase().getCollection("notificationTemplates", NotificationTemplate.class); + private static final MongoCollection notificationTemplatesCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("notificationTemplates", NotificationTemplate.class); @Getter @Id private String id; @Getter @Setter private String subject; diff --git a/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java b/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java index 3896c63..c25fd70 100644 --- a/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java +++ b/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java @@ -2,12 +2,13 @@ package net.frozenorb.apiv3.model; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.sms.SmsCarrierInfo; +import net.frozenorb.apiv3.sms.SmsService; import net.frozenorb.apiv3.util.PhoneUtils; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; -import net.frozenorb.apiv3.util.ZangUtils; -import net.frozenorb.apiv3.zang.ZangResult; import org.bson.Document; @@ -24,11 +25,11 @@ import lombok.Getter; @AllArgsConstructor public final class PhoneIntel { - private static final MongoCollection phoneIntelCollection = APIv3.getDatabase().getCollection("phoneIntel", PhoneIntel.class); + private static final MongoCollection phoneIntelCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("phoneIntel", PhoneIntel.class); @Getter @Id private String id; @Getter private Instant lastUpdatedAt; - @Getter private ZangResult result; + @Getter private SmsCarrierInfo result; public static void findAll(SingleResultCallback> callback) { phoneIntelCollection.find().sort(new Document("lastSeenAt", -1)).into(new LinkedList<>(), SyncUtils.vertxWrap(callback)); @@ -63,13 +64,14 @@ public final class PhoneIntel { return; } - ZangUtils.getCarrierInfo(e164Phone, (zangResult, error2) -> { + + SpringUtils.getBean(SmsService.class).lookupCarrierInfo(e164Phone, (smsCarrierInfo, error2) -> { if (error2 != null) { callback.onResult(null, error2); return; } - PhoneIntel newPhoneIntel = new PhoneIntel(e164Phone, zangResult); + PhoneIntel newPhoneIntel = new PhoneIntel(e164Phone, smsCarrierInfo); phoneIntelCollection.insertOne(newPhoneIntel, SyncUtils.vertxWrap((ignored, error3) -> { if (error3 != null) { @@ -84,7 +86,7 @@ public final class PhoneIntel { private PhoneIntel() {} // For Jackson - private PhoneIntel(String phoneNumber, ZangResult result) { + private PhoneIntel(String phoneNumber, SmsCarrierInfo result) { this.id = phoneNumber; this.lastUpdatedAt = Instant.now(); this.result = result; diff --git a/src/main/java/net/frozenorb/apiv3/model/Punishment.java b/src/main/java/net/frozenorb/apiv3/model/Punishment.java index 0a3cd1d..10c1b74 100644 --- a/src/main/java/net/frozenorb/apiv3/model/Punishment.java +++ b/src/main/java/net/frozenorb/apiv3/model/Punishment.java @@ -2,11 +2,12 @@ package net.frozenorb.apiv3.model; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actor.Actor; import net.frozenorb.apiv3.actor.ActorType; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.TimeUtils; @@ -31,7 +32,7 @@ import lombok.Getter; @AllArgsConstructor public final class Punishment { - private static final MongoCollection punishmentsCollection = APIv3.getDatabase().getCollection("punishments", Punishment.class); + private static final MongoCollection punishmentsCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("punishments", Punishment.class); @Getter @Id private String id; @Getter private UUID user; diff --git a/src/main/java/net/frozenorb/apiv3/model/Rank.java b/src/main/java/net/frozenorb/apiv3/model/Rank.java index 53b54de..61654ab 100644 --- a/src/main/java/net/frozenorb/apiv3/model/Rank.java +++ b/src/main/java/net/frozenorb/apiv3/model/Rank.java @@ -4,9 +4,10 @@ import com.google.common.collect.ImmutableList; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -19,12 +20,13 @@ import java.util.concurrent.TimeUnit; import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; +import io.vertx.core.Vertx; import lombok.Getter; @Entity public final class Rank { - private static final MongoCollection ranksCollection = APIv3.getDatabase().getCollection("ranks", Rank.class); + private static final MongoCollection ranksCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("ranks", Rank.class); private static Map rankIdCache = null; private static List rankCache = null; @@ -51,7 +53,7 @@ public final class Rank { } static { - APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateCache()); + SpringUtils.getBean(Vertx.class).setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateCache()); } public static void updateCache() { diff --git a/src/main/java/net/frozenorb/apiv3/model/Server.java b/src/main/java/net/frozenorb/apiv3/model/Server.java index fd17273..6ee0c79 100644 --- a/src/main/java/net/frozenorb/apiv3/model/Server.java +++ b/src/main/java/net/frozenorb/apiv3/model/Server.java @@ -5,10 +5,11 @@ import com.google.common.collect.ImmutableSet; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.TimeUtils; @@ -26,13 +27,14 @@ import java.util.concurrent.TimeUnit; import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; +import io.vertx.core.Vertx; import lombok.Getter; import lombok.Setter; @Entity public final class Server { - private static final MongoCollection serversCollection = APIv3.getDatabase().getCollection("servers", Server.class); + private static final MongoCollection serversCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("servers", Server.class); private static Map serverIdCache = null; private static List serverCache = null; @@ -54,8 +56,8 @@ public final class Server { } static { - APIv3.getVertxInstance().setPeriodic(TimeUnit.SECONDS.toMillis(15), (id) -> updateCache()); - APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateTimedOutServers()); + SpringUtils.getBean(Vertx.class).setPeriodic(TimeUnit.SECONDS.toMillis(15), (id) -> updateCache()); + SpringUtils.getBean(Vertx.class).setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateTimedOutServers()); } public static void updateCache() { diff --git a/src/main/java/net/frozenorb/apiv3/model/ServerGroup.java b/src/main/java/net/frozenorb/apiv3/model/ServerGroup.java index 7a3d06c..7377807 100644 --- a/src/main/java/net/frozenorb/apiv3/model/ServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/model/ServerGroup.java @@ -5,11 +5,12 @@ import com.google.common.collect.ImmutableMap; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; import net.frozenorb.apiv3.util.PermissionUtils; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -23,6 +24,7 @@ import java.util.concurrent.TimeUnit; import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.objectId.Id; +import io.vertx.core.Vertx; import lombok.Getter; import lombok.Setter; @@ -30,7 +32,7 @@ import lombok.Setter; public final class ServerGroup { public static final String DEFAULT_GROUP_ID = "default"; - private static final MongoCollection serverGroupsCollection = APIv3.getDatabase().getCollection("serverGroups", ServerGroup.class); + private static final MongoCollection serverGroupsCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("serverGroups", ServerGroup.class); private static Map serverGroupIdCache = null; private static List serverGroupCache = null; @@ -53,7 +55,7 @@ public final class ServerGroup { } static { - APIv3.getVertxInstance().setPeriodic(TimeUnit.SECONDS.toMillis(15), (id) -> updateCache()); + SpringUtils.getBean(Vertx.class).setPeriodic(TimeUnit.SECONDS.toMillis(15), (id) -> updateCache()); } public static void updateCache() { diff --git a/src/main/java/net/frozenorb/apiv3/model/User.java b/src/main/java/net/frozenorb/apiv3/model/User.java index c3aac6d..28762f5 100644 --- a/src/main/java/net/frozenorb/apiv3/model/User.java +++ b/src/main/java/net/frozenorb/apiv3/model/User.java @@ -12,24 +12,25 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.mongodb.async.SingleResultCallback; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; -import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.maxmind.MaxMindResult; -import net.frozenorb.apiv3.maxmind.MaxMindUserType; +import net.frozenorb.apiv3.geoip.GeoIpInfo; +import net.frozenorb.apiv3.geoip.GeoIpUserType; +import net.frozenorb.apiv3.mojang.MojangService; import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies; import net.frozenorb.apiv3.serialization.jackson.UuidJsonDeserializer; import net.frozenorb.apiv3.serialization.jackson.UuidJsonSerializer; +import net.frozenorb.apiv3.totp.RequiresTotpResult; +import net.frozenorb.apiv3.totp.TotpAuthorizationResult; +import net.frozenorb.apiv3.totp.TotpService; import net.frozenorb.apiv3.unsorted.MongoToVertxCallback; import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback; import net.frozenorb.apiv3.unsorted.Permissions; -import net.frozenorb.apiv3.unsorted.RequiresTotpResult; -import net.frozenorb.apiv3.unsorted.TotpAuthorizationResult; import net.frozenorb.apiv3.util.IpUtils; -import net.frozenorb.apiv3.util.MojangUtils; import net.frozenorb.apiv3.util.PermissionUtils; import net.frozenorb.apiv3.util.PhoneUtils; +import net.frozenorb.apiv3.util.SpringUtils; import net.frozenorb.apiv3.util.SyncUtils; -import net.frozenorb.apiv3.util.TotpUtils; import net.frozenorb.apiv3.util.UuidUtils; import org.bson.Document; @@ -60,7 +61,7 @@ import lombok.Setter; @AllArgsConstructor public final class User { - private static final MongoCollection usersCollection = APIv3.getDatabase().getCollection("users", User.class); + private static final MongoCollection usersCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("users", User.class); @Getter @Id @JsonSerialize(using = UuidJsonSerializer.class) @JsonDeserialize(using = UuidJsonDeserializer.class) private UUID id; @Getter private String lastUsername; @@ -276,7 +277,7 @@ public final class User { return; } - MojangUtils.getName(collision.getId(), (collisionNewUsername, error2) -> { + SpringUtils.getBean(MojangService.class).getName(collision.getId(), (collisionNewUsername, error2) -> { if (error2 != null) { callback.onResult(null, error2); return; @@ -427,8 +428,8 @@ public final class User { } }); } else if (ipIntel != null) { - MaxMindResult maxMindResult = ipIntel.getResult(); - MaxMindUserType userType = maxMindResult.getTraits().getUserType(); + GeoIpInfo geoIpInfo = ipIntel.getResult(); + GeoIpUserType userType = geoIpInfo.getTraits().getUserType(); Map proposedAccess = null; if (!userType.isAllowed()) { @@ -436,7 +437,7 @@ public final class User { "allowed", false, "message", "You cannot join the server from a VPN." ); - } else if (BannedAsn.findById(maxMindResult.getTraits().getAsn()) != null) { + } else if (BannedAsn.findById(geoIpInfo.getTraits().getAsn()) != null) { proposedAccess = ImmutableMap.of( "allowed", false, "message", "You cannot join the server from this ISP." @@ -521,7 +522,9 @@ public final class User { return; } - TotpUtils.isPreAuthorized(this, ip, (ipPreAuth, error) -> { + TotpService totpService = SpringUtils.getBean(TotpService.class); + + totpService.isPreAuthorized(this, ip, (ipPreAuth, error) -> { if (error != null) { callback.onResult(null, error); } else if (ipPreAuth) { @@ -546,7 +549,9 @@ public final class User { return; } - TotpUtils.isPreAuthorized(this, ip, (preAuthorized, error) -> { + TotpService totpService = SpringUtils.getBean(TotpService.class); + + totpService.isPreAuthorized(this, ip, (preAuthorized, error) -> { if (error != null) { callback.onResult(null, error); return; @@ -557,7 +562,7 @@ public final class User { return; } - TotpUtils.wasRecentlyUsed(this, code, (recentlyUsed, error2) -> { + totpService.wasRecentlyUsed(this, code, (recentlyUsed, error2) -> { if (error2 != null) { callback.onResult(null, error2); return; @@ -568,7 +573,7 @@ public final class User { return; } - if (!TotpUtils.authorizeUser(totpSecret, code)) { + if (!totpService.authorizeUser(totpSecret, code)) { callback.onResult(TotpAuthorizationResult.NOT_AUTHORIZED_BAD_CODE, null); return; } @@ -576,8 +581,8 @@ public final class User { Future markPreAuthFuture = Future.future(); Future markRecentlyUsedFuture = Future.future(); - TotpUtils.markPreAuthorized(this, ip, 3, TimeUnit.DAYS, new MongoToVertxCallback<>(markPreAuthFuture)); - TotpUtils.markRecentlyUsed(this, code, new MongoToVertxCallback<>(markRecentlyUsedFuture)); + totpService.markPreAuthorized(this, ip, 3, TimeUnit.DAYS, new MongoToVertxCallback<>(markPreAuthFuture)); + totpService.markRecentlyUsed(this, code, new MongoToVertxCallback<>(markRecentlyUsedFuture)); CompositeFuture.all(markPreAuthFuture, markRecentlyUsedFuture).setHandler((result) -> { if (result.failed()) { diff --git a/src/main/java/net/frozenorb/apiv3/mojang/HttpMojangService.java b/src/main/java/net/frozenorb/apiv3/mojang/HttpMojangService.java new file mode 100644 index 0000000..877dc6d --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/mojang/HttpMojangService.java @@ -0,0 +1,42 @@ +package net.frozenorb.apiv3.mojang; + +import com.mongodb.async.SingleResultCallback; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.UUID; + +import io.vertx.core.http.HttpClient; +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonObject; + +@Component +public final class HttpMojangService implements MojangService { + + @Autowired private HttpClient httpsClient; + + @Override + public void getName(UUID id, SingleResultCallback callback) { + httpsClient.get(443, "sessionserver.mojang.com", "/session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> { + response.bodyHandler((body) -> { + try { + JsonObject bodyJson = new JsonObject(body.toString()); + String name = bodyJson.getString("name"); + + if (name == null) { + callback.onResult(null, new IOException("Hit Mojang API rate limit: " + bodyJson.encode())); + } else { + callback.onResult(name, null); + } + } catch (DecodeException ex) { + callback.onResult(null, ex); + } + }); + + response.exceptionHandler((error) -> callback.onResult(null, error)); + }).end(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/mojang/MojangService.java b/src/main/java/net/frozenorb/apiv3/mojang/MojangService.java new file mode 100644 index 0000000..219e4a2 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/mojang/MojangService.java @@ -0,0 +1,14 @@ +package net.frozenorb.apiv3.mojang; + +import com.mongodb.async.SingleResultCallback; + +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public interface MojangService { + + void getName(UUID id, SingleResultCallback callback); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java b/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java index 725b250..8ef8b9b 100644 --- a/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java +++ b/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java @@ -18,6 +18,9 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.GeoJsonPoint; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; @@ -27,26 +30,24 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.TimeUnit; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETDumpsType implements Handler { - private static final DecimalFormat coordinateFormat = new DecimalFormat("#.#####"); + private final DecimalFormat coordinateFormat = new DecimalFormat("#.#####"); - private static List banCache = ImmutableList.of(); - private static List blacklistCache = ImmutableList.of(); - private static List ipBanCache = ImmutableList.of(); - private static Map>> grantCache = ImmutableMap.of(); - private static Map ipIntelCache = ImmutableMap.of(); + private List banCache = ImmutableList.of(); + private List blacklistCache = ImmutableList.of(); + private List ipBanCache = ImmutableList.of(); + private Map>> grantCache = ImmutableMap.of(); + private Map ipIntelCache = ImmutableMap.of(); - static { - APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(5), (id) -> updateCache()); - } - - public static void updateCache() { + // 5 minutes, can't use TimeUnit expression in annotation + @Scheduled(fixedRate = 5 * 60 * 1000) + public void updateCache() { Punishment.findByType(ImmutableSet.of( Punishment.PunishmentType.BAN, Punishment.PunishmentType.BLACKLIST @@ -71,8 +72,8 @@ public final class GETDumpsType implements Handler { } } - GETDumpsType.banCache = banCache; - GETDumpsType.blacklistCache = blacklistCache; + this.banCache = banCache; + this.blacklistCache = blacklistCache; }); Grant.findAll((grants, error) -> { @@ -109,7 +110,7 @@ public final class GETDumpsType implements Handler { } } - GETDumpsType.grantCache = grantCache; + this.grantCache = grantCache; }); IpBan.find((ipBans, error) -> { @@ -128,7 +129,7 @@ public final class GETDumpsType implements Handler { ipBanCache.add(ipBan.getUserIp()); } - GETDumpsType.ipBanCache = ipBanCache; + this.ipBanCache = ipBanCache; }); IpIntel.findAllNoSort((ipIntel, error) -> { @@ -148,7 +149,7 @@ public final class GETDumpsType implements Handler { } } - GETDumpsType.ipIntelCache = ipIntelCache; + this.ipIntelCache = ipIntelCache; }); } diff --git a/src/main/java/net/frozenorb/apiv3/route/GETMetrics.java b/src/main/java/net/frozenorb/apiv3/route/GETMetrics.java deleted file mode 100644 index b97ace3..0000000 --- a/src/main/java/net/frozenorb/apiv3/route/GETMetrics.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.frozenorb.apiv3.route; - -import net.frozenorb.apiv3.APIv3; - -import org.bson.Document; - -import io.vertx.core.Handler; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.dropwizard.MetricsService; -import io.vertx.ext.web.RoutingContext; - -public final class GETMetrics implements Handler { - - public void handle(RoutingContext ctx) { - MetricsService metricsService = MetricsService.create(APIv3.getVertxInstance()); - JsonObject metrics = metricsService.getMetricsSnapshot(APIv3.getVertxInstance()); - - // We encode and then parse because JsonObject and Gson don't play nicely with each other - APIv3.respondJson(ctx, 200, Document.parse(metrics.encode())); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/route/GETSearch.java b/src/main/java/net/frozenorb/apiv3/route/GETSearch.java index e8bd2ef..474077c 100644 --- a/src/main/java/net/frozenorb/apiv3/route/GETSearch.java +++ b/src/main/java/net/frozenorb/apiv3/route/GETSearch.java @@ -5,9 +5,12 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETSearch implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/GETWhoAmI.java b/src/main/java/net/frozenorb/apiv3/route/GETWhoAmI.java index a02a74e..377d749 100644 --- a/src/main/java/net/frozenorb/apiv3/route/GETWhoAmI.java +++ b/src/main/java/net/frozenorb/apiv3/route/GETWhoAmI.java @@ -5,9 +5,12 @@ import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actor.Actor; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETWhoAmI implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/POSTLogout.java b/src/main/java/net/frozenorb/apiv3/route/POSTLogout.java index bd80219..b48b9df 100644 --- a/src/main/java/net/frozenorb/apiv3/route/POSTLogout.java +++ b/src/main/java/net/frozenorb/apiv3/route/POSTLogout.java @@ -3,19 +3,25 @@ package net.frozenorb.apiv3.route; import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.usersession.UserSessionService; import net.frozenorb.apiv3.util.ErrorUtils; -import net.frozenorb.apiv3.util.UserSessionUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTLogout implements Handler { + @Autowired private UserSessionService userSessionService; + public void handle(RoutingContext ctx) { String userSession = ctx.request().getHeader("MHQ-UserSession"); String userIp = ctx.request().getHeader("MHQ-UserIp"); - UserSessionUtils.invalidateSession(userIp, userSession, (ignored, error) -> { + userSessionService.invalidateSession(userIp, userSession, (ignored, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); } else { diff --git a/src/main/java/net/frozenorb/apiv3/route/accessTokens/DELETEAccessTokensId.java b/src/main/java/net/frozenorb/apiv3/route/accessTokens/DELETEAccessTokensId.java index f4e17e7..bd32b35 100644 --- a/src/main/java/net/frozenorb/apiv3/route/accessTokens/DELETEAccessTokensId.java +++ b/src/main/java/net/frozenorb/apiv3/route/accessTokens/DELETEAccessTokensId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEAccessTokensId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokens.java b/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokens.java index 4b0950b..e3ec4f7 100644 --- a/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokens.java +++ b/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokens.java @@ -3,13 +3,16 @@ package net.frozenorb.apiv3.route.accessTokens; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.AccessToken; import net.frozenorb.apiv3.model.User; -import net.frozenorb.apiv3.unsorted.TotpAuthorizationResult; +import net.frozenorb.apiv3.totp.TotpAuthorizationResult; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETAccessTokens implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokensId.java b/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokensId.java index a464878..9fd5a71 100644 --- a/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokensId.java +++ b/src/main/java/net/frozenorb/apiv3/route/accessTokens/GETAccessTokensId.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.AccessToken; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETAccessTokensId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/accessTokens/POSTAccessTokens.java b/src/main/java/net/frozenorb/apiv3/route/accessTokens/POSTAccessTokens.java index aa26edb..a1349fb 100644 --- a/src/main/java/net/frozenorb/apiv3/route/accessTokens/POSTAccessTokens.java +++ b/src/main/java/net/frozenorb/apiv3/route/accessTokens/POSTAccessTokens.java @@ -8,16 +8,19 @@ import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.model.AccessToken; import net.frozenorb.apiv3.model.User; -import net.frozenorb.apiv3.unsorted.TotpAuthorizationResult; +import net.frozenorb.apiv3.totp.TotpAuthorizationResult; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.util.List; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTAccessTokens implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/auditLog/DELETEAuditLogId.java b/src/main/java/net/frozenorb/apiv3/route/auditLog/DELETEAuditLogId.java index c2f9325..ab76ea0 100644 --- a/src/main/java/net/frozenorb/apiv3/route/auditLog/DELETEAuditLogId.java +++ b/src/main/java/net/frozenorb/apiv3/route/auditLog/DELETEAuditLogId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEAuditLogId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/auditLog/GETAuditLog.java b/src/main/java/net/frozenorb/apiv3/route/auditLog/GETAuditLog.java index 958cebc..a22fcd2 100644 --- a/src/main/java/net/frozenorb/apiv3/route/auditLog/GETAuditLog.java +++ b/src/main/java/net/frozenorb/apiv3/route/auditLog/GETAuditLog.java @@ -6,10 +6,12 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.UuidUtils; import org.bson.Document; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETAuditLog implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/auditLog/POSTAuditLog.java b/src/main/java/net/frozenorb/apiv3/route/auditLog/POSTAuditLog.java index 25e5508..85ae21e 100644 --- a/src/main/java/net/frozenorb/apiv3/route/auditLog/POSTAuditLog.java +++ b/src/main/java/net/frozenorb/apiv3/route/auditLog/POSTAuditLog.java @@ -7,10 +7,13 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTAuditLog implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/DELETEBannedAsnsId.java b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/DELETEBannedAsnsId.java index 89ee8b0..97a8b7d 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/DELETEBannedAsnsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/DELETEBannedAsnsId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEBannedAsnsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsns.java b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsns.java index 8b0ff82..14eec32 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsns.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsns.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.bannedAsns; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.BannedAsn; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETBannedAsns implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsnsId.java b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsnsId.java index 9d763cc..273d33c 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsnsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/GETBannedAsnsId.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.bannedAsns; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.BannedAsn; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETBannedAsnsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/POSTBannedAsns.java b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/POSTBannedAsns.java index 32e61ec..125b6fa 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/POSTBannedAsns.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/POSTBannedAsns.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTBannedAsns implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/PUTBannedAsnsId.java b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/PUTBannedAsnsId.java index 455646c..7332b0c 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedAsns/PUTBannedAsnsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedAsns/PUTBannedAsnsId.java @@ -1,4 +1,7 @@ package net.frozenorb.apiv3.route.bannedAsns; +import org.springframework.stereotype.Component; + +@Component public class PUTBannedAsnsId { } diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/DELETEBannedCellCarriersId.java b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/DELETEBannedCellCarriersId.java index 48c7ae2..e0f8236 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/DELETEBannedCellCarriersId.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/DELETEBannedCellCarriersId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEBannedCellCarriersId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriers.java b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriers.java index 08a016f..ed9a0cc 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriers.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriers.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.bannedCellCarriers; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.BannedCellCarrier; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETBannedCellCarriers implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriersId.java b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriersId.java index 41ef711..ebf712e 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriersId.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/GETBannedCellCarriersId.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.bannedCellCarriers; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.BannedCellCarrier; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETBannedCellCarriersId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/POSTBannedCellCarriers.java b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/POSTBannedCellCarriers.java index 6acae45..d7796d6 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/POSTBannedCellCarriers.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/POSTBannedCellCarriers.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTBannedCellCarriers implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/PUTBannedCellCarriersId.java b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/PUTBannedCellCarriersId.java index e0e9370..44146c4 100644 --- a/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/PUTBannedCellCarriersId.java +++ b/src/main/java/net/frozenorb/apiv3/route/bannedCellCarriers/PUTBannedCellCarriersId.java @@ -1,4 +1,7 @@ package net.frozenorb.apiv3.route.bannedCellCarriers; +import org.springframework.stereotype.Component; + +@Component public class PUTBannedCellCarriersId { } diff --git a/src/main/java/net/frozenorb/apiv3/route/chatFilter/DELETEChatFilterId.java b/src/main/java/net/frozenorb/apiv3/route/chatFilter/DELETEChatFilterId.java index b750aec..d2cab32 100644 --- a/src/main/java/net/frozenorb/apiv3/route/chatFilter/DELETEChatFilterId.java +++ b/src/main/java/net/frozenorb/apiv3/route/chatFilter/DELETEChatFilterId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEChatFilterId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilter.java b/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilter.java index b2ddeb3..ec013dc 100644 --- a/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilter.java +++ b/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilter.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.ChatFilterEntry; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETChatFilter implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilterId.java b/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilterId.java index e658404..ba83cdc 100644 --- a/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilterId.java +++ b/src/main/java/net/frozenorb/apiv3/route/chatFilter/GETChatFilterId.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.ChatFilterEntry; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETChatFilterId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/chatFilter/POSTChatFilter.java b/src/main/java/net/frozenorb/apiv3/route/chatFilter/POSTChatFilter.java index 6022551..811ba39 100644 --- a/src/main/java/net/frozenorb/apiv3/route/chatFilter/POSTChatFilter.java +++ b/src/main/java/net/frozenorb/apiv3/route/chatFilter/POSTChatFilter.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTChatFilter implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/chatFilter/PUTChatFilterId.java b/src/main/java/net/frozenorb/apiv3/route/chatFilter/PUTChatFilterId.java index 944db85..6c1db9d 100644 --- a/src/main/java/net/frozenorb/apiv3/route/chatFilter/PUTChatFilterId.java +++ b/src/main/java/net/frozenorb/apiv3/route/chatFilter/PUTChatFilterId.java @@ -1,4 +1,7 @@ package net.frozenorb.apiv3.route.chatFilter; +import org.springframework.stereotype.Component; + +@Component public class PUTChatFilterId { } diff --git a/src/main/java/net/frozenorb/apiv3/route/deployment/POSTDeploymentUpdateServer.java b/src/main/java/net/frozenorb/apiv3/route/deployment/POSTDeploymentUpdateServer.java index ce09b73..f93265b 100644 --- a/src/main/java/net/frozenorb/apiv3/route/deployment/POSTDeploymentUpdateServer.java +++ b/src/main/java/net/frozenorb/apiv3/route/deployment/POSTDeploymentUpdateServer.java @@ -11,12 +11,15 @@ import net.frozenorb.apiv3.model.ServerGroup; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.time.Instant; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTDeploymentUpdateServer implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokens.java b/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokens.java index f16e45c..d701162 100644 --- a/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokens.java +++ b/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokens.java @@ -3,18 +3,24 @@ package net.frozenorb.apiv3.route.disposableLoginTokens; import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.disposablelogintoken.DisposableLoginTokenService; import net.frozenorb.apiv3.model.User; -import net.frozenorb.apiv3.util.DisposableLoginTokenUtils; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTDisposableLoginTokens implements Handler { + @Autowired private DisposableLoginTokenService disposableLoginTokenService; + public void handle(RoutingContext ctx) { JsonObject requestBody = ctx.getBodyAsJson(); User user = SyncUtils.runBlocking(v -> User.findById(requestBody.getString("user"), v)); @@ -30,7 +36,7 @@ public final class POSTDisposableLoginTokens implements Handler return; } - DisposableLoginTokenUtils.createToken(user.getId(), userIp, (token, error) -> { + disposableLoginTokenService.createToken(user.getId(), userIp, (token, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); } else { diff --git a/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokensIdUse.java b/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokensIdUse.java index 501a07f..54e9298 100644 --- a/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokensIdUse.java +++ b/src/main/java/net/frozenorb/apiv3/route/disposableLoginTokens/POSTDisposableLoginTokensIdUse.java @@ -5,18 +5,25 @@ import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; -import net.frozenorb.apiv3.util.DisposableLoginTokenUtils; +import net.frozenorb.apiv3.disposablelogintoken.DisposableLoginTokenService; +import net.frozenorb.apiv3.usersession.UserSessionService; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; -import net.frozenorb.apiv3.util.UserSessionUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTDisposableLoginTokensIdUse implements Handler { + @Autowired private UserSessionService userSessionService; + @Autowired private DisposableLoginTokenService disposableLoginTokenService; + public void handle(RoutingContext ctx) { JsonObject requestBody = ctx.getBodyAsJson(); String disposableLoginToken = ctx.request().getParam("disposableLoginToken"); @@ -32,7 +39,7 @@ public final class POSTDisposableLoginTokensIdUse implements Handler { + disposableLoginTokenService.attemptLogin(disposableLoginToken, userIp, (user, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); return; @@ -43,7 +50,7 @@ public final class POSTDisposableLoginTokensIdUse implements Handler UserSessionUtils.createSession(user.getId(), userIp, v)); + String session = SyncUtils.runBlocking(v -> userSessionService.createSession(user.getId(), userIp, v)); AuditLog.log(user.getId(), userIp, ctx, AuditLogActionType.DISPOSABLE_LOGIN_TOKEN_USE, (ignored, error2) -> { if (error2 != null) { diff --git a/src/main/java/net/frozenorb/apiv3/route/emailTokens/GETEmailTokensIdOwner.java b/src/main/java/net/frozenorb/apiv3/route/emailTokens/GETEmailTokensIdOwner.java index df701df..7fe51a1 100644 --- a/src/main/java/net/frozenorb/apiv3/route/emailTokens/GETEmailTokensIdOwner.java +++ b/src/main/java/net/frozenorb/apiv3/route/emailTokens/GETEmailTokensIdOwner.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETEmailTokensIdOwner implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/emailTokens/POSTEmailTokensIdConfirm.java b/src/main/java/net/frozenorb/apiv3/route/emailTokens/POSTEmailTokensIdConfirm.java index e1d8a62..7796602 100644 --- a/src/main/java/net/frozenorb/apiv3/route/emailTokens/POSTEmailTokensIdConfirm.java +++ b/src/main/java/net/frozenorb/apiv3/route/emailTokens/POSTEmailTokensIdConfirm.java @@ -11,12 +11,15 @@ import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.PasswordUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.util.concurrent.TimeUnit; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTEmailTokensIdConfirm implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/grants/DELETEGrantsId.java b/src/main/java/net/frozenorb/apiv3/route/grants/DELETEGrantsId.java index 5acc618..c325432 100644 --- a/src/main/java/net/frozenorb/apiv3/route/grants/DELETEGrantsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/grants/DELETEGrantsId.java @@ -11,10 +11,13 @@ import net.frozenorb.apiv3.unsorted.Permissions; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEGrantsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/grants/GETGrants.java b/src/main/java/net/frozenorb/apiv3/route/grants/GETGrants.java index 8b9bf59..9e4002e 100644 --- a/src/main/java/net/frozenorb/apiv3/route/grants/GETGrants.java +++ b/src/main/java/net/frozenorb/apiv3/route/grants/GETGrants.java @@ -6,12 +6,14 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.UuidUtils; import org.bson.Document; +import org.springframework.stereotype.Component; import java.util.stream.Collectors; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETGrants implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/grants/GETGrantsId.java b/src/main/java/net/frozenorb/apiv3/route/grants/GETGrantsId.java index bd154b4..5be5237 100644 --- a/src/main/java/net/frozenorb/apiv3/route/grants/GETGrantsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/grants/GETGrantsId.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.Grant; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETGrantsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/grants/POSTGrants.java b/src/main/java/net/frozenorb/apiv3/route/grants/POSTGrants.java index 9a41f71..59b28b7 100644 --- a/src/main/java/net/frozenorb/apiv3/route/grants/POSTGrants.java +++ b/src/main/java/net/frozenorb/apiv3/route/grants/POSTGrants.java @@ -9,11 +9,13 @@ import net.frozenorb.apiv3.model.Grant; import net.frozenorb.apiv3.model.Rank; import net.frozenorb.apiv3.model.ServerGroup; import net.frozenorb.apiv3.model.User; +import net.frozenorb.apiv3.totp.TotpAuthorizationResult; import net.frozenorb.apiv3.unsorted.Permissions; -import net.frozenorb.apiv3.unsorted.TotpAuthorizationResult; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.time.Instant; import java.util.HashSet; import java.util.List; @@ -23,6 +25,7 @@ import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTGrants implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ipBans/DELETEIpBansId.java b/src/main/java/net/frozenorb/apiv3/route/ipBans/DELETEIpBansId.java index 2b659bf..3a9b9ee 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ipBans/DELETEIpBansId.java +++ b/src/main/java/net/frozenorb/apiv3/route/ipBans/DELETEIpBansId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEIpBansId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBans.java b/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBans.java index f6021f8..4d11b5e 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBans.java +++ b/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBans.java @@ -6,10 +6,12 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import org.bson.Document; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETIpBans implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBansId.java b/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBansId.java index e73a856..271081e 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBansId.java +++ b/src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBansId.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.IpBan; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETIpBansId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ipBans/POSTIpBans.java b/src/main/java/net/frozenorb/apiv3/route/ipBans/POSTIpBans.java index b8d5594..1b2bc62 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ipBans/POSTIpBans.java +++ b/src/main/java/net/frozenorb/apiv3/route/ipBans/POSTIpBans.java @@ -11,12 +11,15 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.time.Instant; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTIpBans implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ipIntel/GETIpInteld.java b/src/main/java/net/frozenorb/apiv3/route/ipIntel/GETIpInteld.java index dead235..01720e6 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ipIntel/GETIpInteld.java +++ b/src/main/java/net/frozenorb/apiv3/route/ipIntel/GETIpInteld.java @@ -5,9 +5,12 @@ import net.frozenorb.apiv3.model.IpIntel; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETIpInteld implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ipLog/GETIpLogId.java b/src/main/java/net/frozenorb/apiv3/route/ipLog/GETIpLogId.java index 3c0ad64..a6f1008 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ipLog/GETIpLogId.java +++ b/src/main/java/net/frozenorb/apiv3/route/ipLog/GETIpLogId.java @@ -5,11 +5,14 @@ import net.frozenorb.apiv3.model.IpLogEntry; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import java.util.UUID; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETIpLogId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByName.java b/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByName.java index 4e35f61..b85a6f9 100644 --- a/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByName.java +++ b/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByName.java @@ -3,12 +3,15 @@ package net.frozenorb.apiv3.route.lookup; import com.google.common.collect.ImmutableMap; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.HashMap; @@ -20,9 +23,15 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; -public class POSTLookupByName implements Handler { +@Component +public final class POSTLookupByName implements Handler { - private static final MongoCollection usersCollection = APIv3.getDatabase().getCollection("users"); + private final MongoCollection usersCollection; + + @Autowired + public POSTLookupByName(MongoDatabase database) { + this.usersCollection = database.getCollection("users"); + } public void handle(RoutingContext ctx) { JsonObject requestBody = ctx.getBodyAsJson(); diff --git a/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByUuid.java b/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByUuid.java index da54f0a..e785b7d 100644 --- a/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByUuid.java +++ b/src/main/java/net/frozenorb/apiv3/route/lookup/POSTLookupByUuid.java @@ -1,6 +1,7 @@ package net.frozenorb.apiv3.route.lookup; import com.mongodb.async.client.MongoCollection; +import com.mongodb.async.client.MongoDatabase; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.util.ErrorUtils; @@ -8,6 +9,8 @@ import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; import org.bson.Document; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.HashMap; @@ -20,9 +23,15 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTLookupByUuid implements Handler { - private static final MongoCollection usersCollection = APIv3.getDatabase().getCollection("users"); + private final MongoCollection usersCollection; + + @Autowired + public POSTLookupByUuid(MongoDatabase database) { + this.usersCollection = database.getCollection("users"); + } public void handle(RoutingContext ctx) { JsonObject requestBody = ctx.getBodyAsJson(); diff --git a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/DELETENotificationTemplatesId.java b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/DELETENotificationTemplatesId.java index 6c41076..143ace7 100644 --- a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/DELETENotificationTemplatesId.java +++ b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/DELETENotificationTemplatesId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETENotificationTemplatesId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplates.java b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplates.java index a6b62f4..9d3f338 100644 --- a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplates.java +++ b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplates.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETNotificationTemplates implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplatesId.java b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplatesId.java index 4183919..a4d99e0 100644 --- a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplatesId.java +++ b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/GETNotificationTemplatesId.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETNotificationTemplatesId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/POSTNotificationTemplates.java b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/POSTNotificationTemplates.java index 8074468..4a3623d 100644 --- a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/POSTNotificationTemplates.java +++ b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/POSTNotificationTemplates.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTNotificationTemplates implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/PUTNotificationTemplatesId.java b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/PUTNotificationTemplatesId.java index 9ad82a4..af13250 100644 --- a/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/PUTNotificationTemplatesId.java +++ b/src/main/java/net/frozenorb/apiv3/route/notificationTemplates/PUTNotificationTemplatesId.java @@ -1,4 +1,7 @@ package net.frozenorb.apiv3.route.notificationTemplates; +import org.springframework.stereotype.Component; + +@Component public class PUTNotificationTemplatesId { } diff --git a/src/main/java/net/frozenorb/apiv3/route/phoneIntel/GETPhoneInteld.java b/src/main/java/net/frozenorb/apiv3/route/phoneIntel/GETPhoneInteld.java index 9227d3f..aa372a3 100644 --- a/src/main/java/net/frozenorb/apiv3/route/phoneIntel/GETPhoneInteld.java +++ b/src/main/java/net/frozenorb/apiv3/route/phoneIntel/GETPhoneInteld.java @@ -5,9 +5,12 @@ import net.frozenorb.apiv3.model.PhoneIntel; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.PhoneUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETPhoneInteld implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEPunishmentsId.java b/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEPunishmentsId.java index 2e5d1d1..103b3da 100644 --- a/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEPunishmentsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEPunishmentsId.java @@ -11,10 +11,13 @@ import net.frozenorb.apiv3.unsorted.Permissions; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEPunishmentsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEUsersIdActivePunishment.java b/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEUsersIdActivePunishment.java index bccd4ba..b05727a 100644 --- a/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEUsersIdActivePunishment.java +++ b/src/main/java/net/frozenorb/apiv3/route/punishments/DELETEUsersIdActivePunishment.java @@ -13,6 +13,8 @@ import net.frozenorb.apiv3.unsorted.Permissions; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.util.LinkedList; import java.util.List; @@ -20,6 +22,7 @@ import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEUsersIdActivePunishment implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishments.java b/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishments.java index d87fec5..4c73b30 100644 --- a/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishments.java +++ b/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishments.java @@ -6,6 +6,7 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.UuidUtils; import org.bson.Document; +import org.springframework.stereotype.Component; import java.util.UUID; import java.util.stream.Collectors; @@ -13,6 +14,7 @@ import java.util.stream.Collectors; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETPunishments implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishmentsId.java b/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishmentsId.java index add0d94..7f74805 100644 --- a/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishmentsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/punishments/GETPunishmentsId.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.Punishment; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETPunishmentsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/punishments/POSTPunishments.java b/src/main/java/net/frozenorb/apiv3/route/punishments/POSTPunishments.java index 265868a..8c49717 100644 --- a/src/main/java/net/frozenorb/apiv3/route/punishments/POSTPunishments.java +++ b/src/main/java/net/frozenorb/apiv3/route/punishments/POSTPunishments.java @@ -15,6 +15,8 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.time.Instant; import java.util.List; import java.util.Map; @@ -23,6 +25,7 @@ import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTPunishments implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ranks/DELETERanksId.java b/src/main/java/net/frozenorb/apiv3/route/ranks/DELETERanksId.java index 5bc164e..af6596d 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ranks/DELETERanksId.java +++ b/src/main/java/net/frozenorb/apiv3/route/ranks/DELETERanksId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETERanksId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanks.java b/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanks.java index 2eacb2f..3a5ee2e 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanks.java +++ b/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanks.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.ranks; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.Rank; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETRanks implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanksId.java b/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanksId.java index 6bac235..10f3bf3 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanksId.java +++ b/src/main/java/net/frozenorb/apiv3/route/ranks/GETRanksId.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.ranks; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.Rank; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETRanksId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ranks/POSTRanks.java b/src/main/java/net/frozenorb/apiv3/route/ranks/POSTRanks.java index 1c0c1ac..122c772 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ranks/POSTRanks.java +++ b/src/main/java/net/frozenorb/apiv3/route/ranks/POSTRanks.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTRanks implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/ranks/PUTRanksId.java b/src/main/java/net/frozenorb/apiv3/route/ranks/PUTRanksId.java index 60f8db6..b85ad4f 100644 --- a/src/main/java/net/frozenorb/apiv3/route/ranks/PUTRanksId.java +++ b/src/main/java/net/frozenorb/apiv3/route/ranks/PUTRanksId.java @@ -1,4 +1,7 @@ package net.frozenorb.apiv3.route.ranks; +import org.springframework.stereotype.Component; + +@Component public class PUTRanksId { } diff --git a/src/main/java/net/frozenorb/apiv3/route/serverGroups/DELETEServerGroupsId.java b/src/main/java/net/frozenorb/apiv3/route/serverGroups/DELETEServerGroupsId.java index 02d590c..5ddcddf 100644 --- a/src/main/java/net/frozenorb/apiv3/route/serverGroups/DELETEServerGroupsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/serverGroups/DELETEServerGroupsId.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEServerGroupsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroups.java b/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroups.java index 620b028..9c23978 100644 --- a/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroups.java +++ b/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroups.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.serverGroups; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.ServerGroup; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETServerGroups implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroupsId.java b/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroupsId.java index 3fc61a8..5b21210 100644 --- a/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroupsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/serverGroups/GETServerGroupsId.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.serverGroups; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.ServerGroup; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETServerGroupsId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/serverGroups/POSTServerGroups.java b/src/main/java/net/frozenorb/apiv3/route/serverGroups/POSTServerGroups.java index 3ea9dac..f399e2e 100644 --- a/src/main/java/net/frozenorb/apiv3/route/serverGroups/POSTServerGroups.java +++ b/src/main/java/net/frozenorb/apiv3/route/serverGroups/POSTServerGroups.java @@ -10,10 +10,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTServerGroups implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/serverGroups/PUTServerGroupsId.java b/src/main/java/net/frozenorb/apiv3/route/serverGroups/PUTServerGroupsId.java index 8128ceb..167d87f 100644 --- a/src/main/java/net/frozenorb/apiv3/route/serverGroups/PUTServerGroupsId.java +++ b/src/main/java/net/frozenorb/apiv3/route/serverGroups/PUTServerGroupsId.java @@ -1,4 +1,7 @@ package net.frozenorb.apiv3.route.serverGroups; +import org.springframework.stereotype.Component; + +@Component public class PUTServerGroupsId { } diff --git a/src/main/java/net/frozenorb/apiv3/route/servers/DELETEServersId.java b/src/main/java/net/frozenorb/apiv3/route/servers/DELETEServersId.java index c1e880d..82ce650 100644 --- a/src/main/java/net/frozenorb/apiv3/route/servers/DELETEServersId.java +++ b/src/main/java/net/frozenorb/apiv3/route/servers/DELETEServersId.java @@ -12,10 +12,13 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class DELETEServersId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/servers/GETServers.java b/src/main/java/net/frozenorb/apiv3/route/servers/GETServers.java index 4fd2c20..d6ca2c0 100644 --- a/src/main/java/net/frozenorb/apiv3/route/servers/GETServers.java +++ b/src/main/java/net/frozenorb/apiv3/route/servers/GETServers.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.servers; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.Server; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETServers implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/servers/GETServersId.java b/src/main/java/net/frozenorb/apiv3/route/servers/GETServersId.java index e5ba750..3504777 100644 --- a/src/main/java/net/frozenorb/apiv3/route/servers/GETServersId.java +++ b/src/main/java/net/frozenorb/apiv3/route/servers/GETServersId.java @@ -3,9 +3,12 @@ package net.frozenorb.apiv3.route.servers; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.Server; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETServersId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/servers/POSTServers.java b/src/main/java/net/frozenorb/apiv3/route/servers/POSTServers.java index c129dd6..3ee2425 100644 --- a/src/main/java/net/frozenorb/apiv3/route/servers/POSTServers.java +++ b/src/main/java/net/frozenorb/apiv3/route/servers/POSTServers.java @@ -13,10 +13,13 @@ import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTServers implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java b/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java index 1db5fe6..b3ce56b 100644 --- a/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java +++ b/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java @@ -19,6 +19,8 @@ import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.PermissionUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -33,6 +35,7 @@ import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import lombok.extern.slf4j.Slf4j; +@Component @Slf4j public final class POSTServersHeartbeat implements Handler { diff --git a/src/main/java/net/frozenorb/apiv3/route/servers/PUTServersId.java b/src/main/java/net/frozenorb/apiv3/route/servers/PUTServersId.java index c67720d..32016b2 100644 --- a/src/main/java/net/frozenorb/apiv3/route/servers/PUTServersId.java +++ b/src/main/java/net/frozenorb/apiv3/route/servers/PUTServersId.java @@ -1,4 +1,7 @@ package net.frozenorb.apiv3.route.servers; +import org.springframework.stereotype.Component; + +@Component public class PUTServersId { } diff --git a/src/main/java/net/frozenorb/apiv3/route/users/GETStaff.java b/src/main/java/net/frozenorb/apiv3/route/users/GETStaff.java index f1b35f5..faa9dac 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/GETStaff.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/GETStaff.java @@ -6,6 +6,8 @@ import net.frozenorb.apiv3.model.Rank; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -15,6 +17,7 @@ import java.util.TreeMap; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETStaff implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersId.java b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersId.java index c81a1b0..ce24908 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersId.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersId.java @@ -4,9 +4,12 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETUsersId implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdCompoundedPermissions.java b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdCompoundedPermissions.java index 0db75c5..0adf761 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdCompoundedPermissions.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdCompoundedPermissions.java @@ -5,9 +5,12 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.PermissionUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETUsersIdCompoundedPermissions implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdDetails.java b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdDetails.java index 5c6ebcf..a9188bf 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdDetails.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdDetails.java @@ -8,6 +8,8 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -15,6 +17,7 @@ import java.util.Map; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETUsersIdDetails implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdRequiresTotp.java b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdRequiresTotp.java index f1bc16f..177f9f2 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdRequiresTotp.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdRequiresTotp.java @@ -4,13 +4,16 @@ import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.User; -import net.frozenorb.apiv3.unsorted.RequiresTotpResult; +import net.frozenorb.apiv3.totp.RequiresTotpResult; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETUsersIdRequiresTotp implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdVerifyPassword.java b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdVerifyPassword.java index 6d64fce..edbeb22 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdVerifyPassword.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/GETUsersIdVerifyPassword.java @@ -6,10 +6,13 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.model.User; +import net.frozenorb.apiv3.usersession.UserSessionService; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; -import net.frozenorb.apiv3.util.UserSessionUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; @@ -18,8 +21,11 @@ import java.util.UUID; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +@Component public final class GETUsersIdVerifyPassword implements Handler { + @Autowired private UserSessionService userSessionService; + public void handle(RoutingContext ctx) { User user = SyncUtils.runBlocking(v -> User.findById(ctx.request().getParam("userId"), v)); @@ -75,7 +81,7 @@ public final class GETUsersIdVerifyPassword implements Handler { }; if (authorized) { - UserSessionUtils.createSession(finalUuid, userIp, responseCallback); + userSessionService.createSession(finalUuid, userIp, responseCallback); } else { responseCallback.onResult(null, null); } diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdChangePassword.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdChangePassword.java index 74ac2fa..5002212 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdChangePassword.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdChangePassword.java @@ -6,20 +6,26 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.model.User; -import net.frozenorb.apiv3.unsorted.RequiresTotpResult; -import net.frozenorb.apiv3.unsorted.TotpAuthorizationResult; +import net.frozenorb.apiv3.totp.RequiresTotpResult; +import net.frozenorb.apiv3.totp.TotpAuthorizationResult; +import net.frozenorb.apiv3.usersession.UserSessionService; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.PasswordUtils; import net.frozenorb.apiv3.util.SyncUtils; -import net.frozenorb.apiv3.util.UserSessionUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdChangePassword implements Handler { + @Autowired private UserSessionService userSessionService; + public void handle(RoutingContext ctx) { User user = SyncUtils.runBlocking(v -> User.findById(ctx.request().getParam("userId"), v)); JsonObject requestBody = ctx.getBodyAsJson(); @@ -70,7 +76,7 @@ public final class POSTUsersIdChangePassword implements Handler user.updatePassword(newPassword); SyncUtils.runBlocking(v -> user.save(v)); - SyncUtils.runBlocking(v -> UserSessionUtils.invalidateAllSessions(user.getId(), v)); + SyncUtils.runBlocking(v -> userSessionService.invalidateAllSessions(user.getId(), v)); String userIp = requestBody.getString("userIp"); if (!IpUtils.isValidIp(userIp)) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java index 2582e81..4387051 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java @@ -10,12 +10,15 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.stereotype.Component; + import java.util.concurrent.TimeUnit; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdConfirmPhone implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java index d738a43..eada7d7 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java @@ -14,12 +14,15 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.UuidUtils; +import org.springframework.stereotype.Component; + import java.util.UUID; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdLogin implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdNotify.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdNotify.java index be9b960..1b52572 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdNotify.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdNotify.java @@ -3,20 +3,27 @@ package net.frozenorb.apiv3.route.users; import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.email.EmailService; import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import java.util.Map; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdNotify implements Handler { + @Autowired private EmailService emailService; + public void handle(RoutingContext ctx) { User user = SyncUtils.runBlocking(v -> User.findById(ctx.request().getParam("userId"), v)); @@ -43,7 +50,7 @@ public final class POSTUsersIdNotify implements Handler { Notification notification = new Notification(template, subjectReplacements, bodyReplacements); - notification.sendAsEmail(user.getEmail(), (ignored, error) -> { + emailService.sendEmail(notification, user.getEmail(), (ignored, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); } else { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdPasswordReset.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdPasswordReset.java index 645ae8a..6322579 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdPasswordReset.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdPasswordReset.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.email.EmailService; import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.unsorted.Notification; @@ -12,6 +13,9 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import java.util.Map; import java.util.concurrent.TimeUnit; @@ -19,8 +23,11 @@ import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdPasswordReset implements Handler { + @Autowired private EmailService emailService; + public void handle(RoutingContext ctx) { User user = SyncUtils.runBlocking(v -> User.findById(ctx.request().getParam("userId"), v)); @@ -45,7 +52,7 @@ public final class POSTUsersIdPasswordReset implements Handler { NotificationTemplate template = SyncUtils.runBlocking(v -> NotificationTemplate.findById("password-reset", v)); Notification notification = new Notification(template, replacements, replacements); - notification.sendAsEmail(user.getEmail(), (ignored, error) -> { + emailService.sendEmail(notification, user.getEmail(), (ignored, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); } else { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterEmail.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterEmail.java index 695f645..e75d570 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterEmail.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterEmail.java @@ -5,6 +5,8 @@ import com.google.common.collect.ImmutableMap; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.email.EmailDomainService; +import net.frozenorb.apiv3.email.EmailService; import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.unsorted.Notification; @@ -13,6 +15,9 @@ import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import java.util.Map; import java.util.concurrent.TimeUnit; @@ -20,8 +25,12 @@ import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdRegisterEmail implements Handler { + @Autowired private EmailService emailService; + @Autowired private EmailDomainService emailDomainService; + public void handle(RoutingContext ctx) { User user = SyncUtils.runBlocking(v -> User.findById(ctx.request().getParam("userId"), v)); @@ -43,7 +52,7 @@ public final class POSTUsersIdRegisterEmail implements Handler { return; } - if (EmailUtils.isBannedEmailDomain(email)) { + if (emailDomainService.isBannedDomain(email)) { ErrorUtils.respondInvalidInput(ctx, email + " is from a blacklisted domain."); return; } @@ -72,7 +81,7 @@ public final class POSTUsersIdRegisterEmail implements Handler { NotificationTemplate template = SyncUtils.runBlocking(v -> NotificationTemplate.findById("email-confirmation", v)); Notification notification = new Notification(template, replacements, replacements); - notification.sendAsEmail(user.getPendingEmail(), (ignored, error) -> { + emailService.sendEmail(notification, user.getPendingEmail(), (ignored, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); } else { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java index 2e8269c..2fa5e06 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java @@ -9,12 +9,16 @@ import net.frozenorb.apiv3.model.BannedCellCarrier; import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.PhoneIntel; import net.frozenorb.apiv3.model.User; +import net.frozenorb.apiv3.sms.SmsService; import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.PhoneUtils; import net.frozenorb.apiv3.util.SyncUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import java.util.Map; import java.util.concurrent.TimeUnit; @@ -22,8 +26,11 @@ import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdRegisterPhone implements Handler { + @Autowired private SmsService smsService; + public void handle(RoutingContext ctx) { User user = SyncUtils.runBlocking(v -> User.findById(ctx.request().getParam("userId"), v)); @@ -76,7 +83,7 @@ public final class POSTUsersIdRegisterPhone implements Handler { NotificationTemplate template = SyncUtils.runBlocking(v -> NotificationTemplate.findById("phone-confirmation", v)); Notification notification = new Notification(template, replacements, replacements); - notification.sendAsText(user.getPendingPhone(), (ignored, error) -> { + smsService.sendText(notification, user.getPendingPhone(), (ignored, error) -> { if (error != null) { ErrorUtils.respondInternalError(ctx, error); } else { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdSetupTotp.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdSetupTotp.java index 284af8f..81f300b 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdSetupTotp.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdSetupTotp.java @@ -6,17 +6,23 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.model.User; +import net.frozenorb.apiv3.totp.TotpService; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.SyncUtils; -import net.frozenorb.apiv3.util.TotpUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdSetupTotp implements Handler { + @Autowired private TotpService totpService; + public void handle(RoutingContext ctx) { User user = SyncUtils.runBlocking(v -> User.findById(ctx.request().getParam("userId"), v)); @@ -34,7 +40,7 @@ public final class POSTUsersIdSetupTotp implements Handler { String secret = requestBody.getString("secret"); int totpCode = requestBody.getInteger("totpCode", -1); - if (TotpUtils.authorizeUser(secret, totpCode)) { + if (totpService.authorizeUser(secret, totpCode)) { user.setTotpSecret(secret); SyncUtils.runBlocking(v -> user.save(v)); String userIp = requestBody.getString("userIp"); diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdVerifyTotp.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdVerifyTotp.java index 5161800..42f6408 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdVerifyTotp.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdVerifyTotp.java @@ -9,10 +9,13 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; +import org.springframework.stereotype.Component; + import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersIdVerifyTotp implements Handler { public void handle(RoutingContext ctx) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersUsePasswordResetToken.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersUsePasswordResetToken.java index 8a7a6bb..e6a2edd 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersUsePasswordResetToken.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersUsePasswordResetToken.java @@ -6,11 +6,14 @@ import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.model.User; +import net.frozenorb.apiv3.usersession.UserSessionService; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; import net.frozenorb.apiv3.util.PasswordUtils; import net.frozenorb.apiv3.util.SyncUtils; -import net.frozenorb.apiv3.util.UserSessionUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @@ -18,8 +21,11 @@ import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@Component public final class POSTUsersUsePasswordResetToken implements Handler { + @Autowired private UserSessionService userSessionService; + public void handle(RoutingContext ctx) { JsonObject requestBody = ctx.getBodyAsJson(); @@ -55,7 +61,7 @@ public final class POSTUsersUsePasswordResetToken implements HandlerrunBlocking(v -> user.save(v)); - SyncUtils.runBlocking(v -> UserSessionUtils.invalidateAllSessions(user.getId(), v)); + SyncUtils.runBlocking(v -> userSessionService.invalidateAllSessions(user.getId(), v)); String userIp = requestBody.getString("userIp"); if (!IpUtils.isValidIp(userIp)) { diff --git a/src/main/java/net/frozenorb/apiv3/zang/ZangResult.java b/src/main/java/net/frozenorb/apiv3/sms/SmsCarrierInfo.java similarity index 77% rename from src/main/java/net/frozenorb/apiv3/zang/ZangResult.java rename to src/main/java/net/frozenorb/apiv3/sms/SmsCarrierInfo.java index 2e5d0dd..045788d 100644 --- a/src/main/java/net/frozenorb/apiv3/zang/ZangResult.java +++ b/src/main/java/net/frozenorb/apiv3/sms/SmsCarrierInfo.java @@ -1,9 +1,9 @@ -package net.frozenorb.apiv3.zang; +package net.frozenorb.apiv3.sms; import io.vertx.core.json.JsonObject; import lombok.Getter; -public final class ZangResult { +public final class SmsCarrierInfo { @Getter private String phoneNumber; @Getter private String countryCode; @@ -11,9 +11,9 @@ public final class ZangResult { @Getter private String network; @Getter private boolean mobile; - private ZangResult() {} // For Jackson + private SmsCarrierInfo() {} // For Jackson - public ZangResult(JsonObject legacy) { + SmsCarrierInfo(JsonObject legacy) { this.phoneNumber = legacy.getString("phone_number"); this.countryCode = legacy.getString("country_code"); this.carrierId = legacy.getInteger("carrier_id", -1); diff --git a/src/main/java/net/frozenorb/apiv3/sms/SmsService.java b/src/main/java/net/frozenorb/apiv3/sms/SmsService.java new file mode 100644 index 0000000..ef64832 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/sms/SmsService.java @@ -0,0 +1,16 @@ +package net.frozenorb.apiv3.sms; + +import com.mongodb.async.SingleResultCallback; + +import net.frozenorb.apiv3.unsorted.Notification; + +import org.springframework.stereotype.Service; + +@Service +public interface SmsService { + + void sendText(Notification notification, String to, SingleResultCallback callback); + + void lookupCarrierInfo(String phoneNumber, SingleResultCallback callback); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/sms/ZangSmsService.java b/src/main/java/net/frozenorb/apiv3/sms/ZangSmsService.java new file mode 100644 index 0000000..a16f332 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/sms/ZangSmsService.java @@ -0,0 +1,72 @@ +package net.frozenorb.apiv3.sms; + +import com.google.common.base.Charsets; +import com.google.common.net.MediaType; + +import com.mongodb.async.SingleResultCallback; + +import net.frozenorb.apiv3.unsorted.Notification; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Base64; + +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpHeaders; +import io.vertx.core.json.JsonObject; + +@Component +public final class ZangSmsService implements SmsService { + + @Autowired private HttpClient httpsClient; + @Value("${zang.accountSid}") private String accountSid; + @Value("${zang.authToken}") private String authToken; + @Value("${zang.fromNumber}") private String fromNumber; + + @Override + public void sendText(Notification notification, String to, SingleResultCallback callback) { + String authHeader = "Basic " + Base64.getEncoder().encodeToString((accountSid + ":" + authToken).getBytes(Charsets.UTF_8)); + + httpsClient.post(443, "api.zang.io", "/v2/Accounts/" + accountSid + "/SMS/Messages.json", (response) -> { + response.bodyHandler((body) -> { + JsonObject bodyJson = new JsonObject(body.toString()); + + if (bodyJson.getString("status", "").equals("queued")) { + callback.onResult(null, null); + } else { + callback.onResult(null, new IOException("Could not send text message: " + bodyJson.encode())); + } + }); + + response.exceptionHandler((error) -> callback.onResult(null, error)); + }).putHeader("Authorization", authHeader).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()).end("To=" + to + "&From=" + fromNumber + "&Body=" + notification.getBody()); + } + + @Override + public void lookupCarrierInfo(String phoneNumber, SingleResultCallback callback) { + String authHeader = "Basic " + Base64.getEncoder().encodeToString((accountSid + ":" + authToken).getBytes(Charsets.UTF_8)); + + httpsClient.post(443, "api.zang.io", "/v2/Accounts/" + accountSid + "/Lookups/Carrier.json", (response) -> { + response.bodyHandler((body) -> { + JsonObject bodyJson = new JsonObject(body.toString()); + + if (bodyJson.containsKey("carrier_lookups")) { + // Zang returns an array, but we don't batch at all so we always just get the first element + JsonObject lookupResult = bodyJson.getJsonArray("carrier_lookups").getJsonObject(0); + callback.onResult(new SmsCarrierInfo(lookupResult), null); + } else { + callback.onResult(null, new IOException("Could not parse Zang result: " + bodyJson.encode())); + } + }); + + response.exceptionHandler((error) -> callback.onResult(null, error)); + }) + .putHeader("Authorization", authHeader) + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()) + .end("PhoneNumber=" + phoneNumber); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/totp/RedisTotpService.java b/src/main/java/net/frozenorb/apiv3/totp/RedisTotpService.java new file mode 100644 index 0000000..87e7f50 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/totp/RedisTotpService.java @@ -0,0 +1,86 @@ +package net.frozenorb.apiv3.totp; + +import com.mongodb.async.SingleResultCallback; +import com.warrenstrange.googleauth.GoogleAuthenticator; +import com.warrenstrange.googleauth.GoogleAuthenticatorConfig; + +import net.frozenorb.apiv3.model.User; +import net.frozenorb.apiv3.util.IpUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import io.vertx.redis.RedisClient; + +@Component +public final class RedisTotpService implements TotpService { + + private final GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build()); + @Autowired private RedisClient redisClient; + + @Override + public boolean authorizeUser(String secret, int code) { + return googleAuthenticator.authorize(secret, code); + } + + @Override + public void isPreAuthorized(User user, String ip, SingleResultCallback callback) { + if (!IpUtils.isValidIp(ip)) { + callback.onResult(false, null); + return; + } + + redisClient.exists(user.getId() + ":preAuthorizedIp:" + ip.toLowerCase(), (result) -> { + if (result.succeeded()) { + callback.onResult(result.result() == 1, null); + } else { + callback.onResult(null, result.cause()); + } + }); + } + + @Override + public void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback callback) { + if (!IpUtils.isValidIp(ip)) { + callback.onResult(null, null); + return; + } + + String key = user.getId() + ":preAuthorizedIp:" + ip.toLowerCase(); + + redisClient.setex(key, unit.toSeconds(duration), "", (result) -> { + if (result.succeeded()) { + callback.onResult(null, null); + } else { + callback.onResult(null, result.cause()); + } + }); + } + + @Override + public void wasRecentlyUsed(User user, int code, SingleResultCallback callback) { + redisClient.exists(user.getId() + ":recentTotpCodes:" + code, (result) -> { + if (result.succeeded()) { + callback.onResult(result.result() == 1, null); + } else { + callback.onResult(null, result.cause()); + } + }); + } + + @Override + public void markRecentlyUsed(User user, int code, SingleResultCallback callback) { + String key = user.getId() + ":recentTotpCodes:" + code; + + redisClient.setex(key, TimeUnit.MINUTES.toSeconds(5), "", (result) -> { + if (result.succeeded()) { + callback.onResult(null, null); + } else { + callback.onResult(null, result.cause()); + } + }); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/RequiresTotpResult.java b/src/main/java/net/frozenorb/apiv3/totp/RequiresTotpResult.java similarity index 74% rename from src/main/java/net/frozenorb/apiv3/unsorted/RequiresTotpResult.java rename to src/main/java/net/frozenorb/apiv3/totp/RequiresTotpResult.java index 905d685..1227b09 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/RequiresTotpResult.java +++ b/src/main/java/net/frozenorb/apiv3/totp/RequiresTotpResult.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.unsorted; +package net.frozenorb.apiv3.totp; public enum RequiresTotpResult { diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/TotpAuthorizationResult.java b/src/main/java/net/frozenorb/apiv3/totp/TotpAuthorizationResult.java similarity index 90% rename from src/main/java/net/frozenorb/apiv3/unsorted/TotpAuthorizationResult.java rename to src/main/java/net/frozenorb/apiv3/totp/TotpAuthorizationResult.java index 5593c95..c703104 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/TotpAuthorizationResult.java +++ b/src/main/java/net/frozenorb/apiv3/totp/TotpAuthorizationResult.java @@ -1,4 +1,4 @@ -package net.frozenorb.apiv3.unsorted; +package net.frozenorb.apiv3.totp; import lombok.Getter; diff --git a/src/main/java/net/frozenorb/apiv3/totp/TotpService.java b/src/main/java/net/frozenorb/apiv3/totp/TotpService.java new file mode 100644 index 0000000..6c973d2 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/totp/TotpService.java @@ -0,0 +1,24 @@ +package net.frozenorb.apiv3.totp; + +import com.mongodb.async.SingleResultCallback; + +import net.frozenorb.apiv3.model.User; + +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +public interface TotpService { + + boolean authorizeUser(String secret, int code); + + void isPreAuthorized(User user, String ip, SingleResultCallback callback); + + void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback callback); + + void wasRecentlyUsed(User user, int code, SingleResultCallback callback); + + void markRecentlyUsed(User user, int code, SingleResultCallback callback); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java index 241314c..01d1d28 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java @@ -1,42 +1,19 @@ package net.frozenorb.apiv3.unsorted; -import com.mongodb.async.SingleResultCallback; - import net.frozenorb.apiv3.model.NotificationTemplate; -import net.frozenorb.apiv3.util.MandrillUtils; -import net.frozenorb.apiv3.util.ZangUtils; import java.util.Map; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; +import lombok.Getter; public final class Notification { - private final String subject; - private final String body; + @Getter private final String subject; + @Getter private final String body; public Notification(NotificationTemplate template, Map subjectReplacements, Map bodyReplacements) { this.subject = template.fillSubject(subjectReplacements); this.body = template.fillBody(bodyReplacements); } - public void sendAsEmail(String email, SingleResultCallback callback) { - JsonObject message = new JsonObject(); - - message.put("html", body); - message.put("subject", subject); - message.put("from_email", "no-reply@minehq.com"); - message.put("from_name", "MineHQ Network"); - message.put("to", new JsonArray().add( - new JsonObject().put("email", email) - )); - - MandrillUtils.sendEmail(message, callback); - } - - public void sendAsText(String phoneNumber, SingleResultCallback callback) { - ZangUtils.sendText(phoneNumber, body, callback); - } - } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/usersession/RedisUserSessionService.java b/src/main/java/net/frozenorb/apiv3/usersession/RedisUserSessionService.java new file mode 100644 index 0000000..61b4f66 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/usersession/RedisUserSessionService.java @@ -0,0 +1,91 @@ +package net.frozenorb.apiv3.usersession; + +import com.mongodb.async.SingleResultCallback; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import io.vertx.redis.RedisClient; + +@Component +public final class RedisUserSessionService implements UserSessionService { + + @Autowired private RedisClient redisClient; + + @Override + public void sessionExists(String userIp, String userSession, SingleResultCallback callback) { + if (userIp == null || userIp.isEmpty() || userSession == null || userSession.isEmpty()) { + callback.onResult(false, null); + return; + } + + redisClient.exists("apiv3:sessions:" + userIp + ":" + userSession, (result) -> { + if (result.succeeded()) { + callback.onResult(result.result() == 1, null); + } else { + callback.onResult(null, result.cause()); + } + }); + } + + @Override + public void createSession(UUID user, String userIp, SingleResultCallback callback) { + String userSession = UUID.randomUUID().toString().replaceAll("-", ""); + String key = "apiv3:sessions:" + userIp + ":" + userSession; + + redisClient.setex(key, TimeUnit.DAYS.toSeconds(30), "", (result) -> { + if (result.succeeded()) { + redisClient.sadd("apiv3:sessions:" + user, key, (result2) -> { + if (result2.succeeded()) { + callback.onResult(userSession, null); + } else { + callback.onResult(null, result2.cause()); + } + }); + } else { + callback.onResult(null, result.cause()); + } + }); + } + + @Override + public void invalidateSession(String userIp, String userSession, SingleResultCallback callback) { + redisClient.del("apiv3:sessions:" + userIp + ":" + userSession, (result) -> { + if (result.succeeded()) { + callback.onResult(null, null); + } else { + callback.onResult(null, result.cause()); + } + }); + } + + @Override + public void invalidateAllSessions(UUID user, SingleResultCallback callback) { + redisClient.smembers("apiv3:sessions:" + user, (result) -> { + if (result.failed()) { + callback.onResult(null, result.cause()); + return; + } + + List sessions = (List) result.result().getList(); + + if (sessions.isEmpty()) { + callback.onResult(null, null); + return; + } + + redisClient.delMany(sessions, (result2) -> { + if (result2.succeeded()) { + callback.onResult(null, null); + } else { + callback.onResult(null, result2.cause()); + } + }); + }); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/usersession/UserSessionService.java b/src/main/java/net/frozenorb/apiv3/usersession/UserSessionService.java new file mode 100644 index 0000000..3af3b02 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/usersession/UserSessionService.java @@ -0,0 +1,20 @@ +package net.frozenorb.apiv3.usersession; + +import com.mongodb.async.SingleResultCallback; + +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public interface UserSessionService { + + void sessionExists(String userIp, String userSession, SingleResultCallback callback); + + void createSession(UUID user, String userIp, SingleResultCallback callback); + + void invalidateSession(String userIp, String userSession, SingleResultCallback callback); + + void invalidateAllSessions(UUID user, SingleResultCallback callback); + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/DisposableLoginTokenUtils.java b/src/main/java/net/frozenorb/apiv3/util/DisposableLoginTokenUtils.java deleted file mode 100644 index b5005c1..0000000 --- a/src/main/java/net/frozenorb/apiv3/util/DisposableLoginTokenUtils.java +++ /dev/null @@ -1,70 +0,0 @@ -package net.frozenorb.apiv3.util; - -import com.mongodb.async.SingleResultCallback; - -import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.model.User; - -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import io.vertx.redis.RedisClient; -import io.vertx.redis.RedisOptions; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class DisposableLoginTokenUtils { - - private static final RedisClient redisClient = RedisClient.create(APIv3.getVertxInstance(), - new RedisOptions() - .setAddress(APIv3.getConfig().getProperty("redis.address")) - .setPort(Integer.parseInt(APIv3.getConfig().getProperty("redis.port"))) - ); - - public static void useToken(String token, String userIp, SingleResultCallback callback) { - if (token == null || token.isEmpty()) { - callback.onResult(null, null); - return; - } - - redisClient.get("apiv3:disposableLoginTokens:" + userIp + ":" + token, (result) -> { - if (result.failed()) { - callback.onResult(null, result.cause()); - return; - } - - if (result.result() == null) { - callback.onResult(null, null); - return; - } - - User.findById(result.result(), (user, error) -> { - if (error != null) { - callback.onResult(null, error); - return; - } - - redisClient.del("apiv3:disposableLoginTokens:" + userIp + ":" + token, (result2) -> { - if (result2.failed()) { - callback.onResult(null, result2.cause()); - } else { - callback.onResult(user, null); - } - }); - }); - }); - } - - public static void createToken(UUID user, String userIp, SingleResultCallback callback) { - String token = UUID.randomUUID().toString().replaceAll("-", ""); - - redisClient.setex("apiv3:disposableLoginTokens:" + userIp + ":" + token, TimeUnit.MINUTES.toSeconds(5), user.toString(), (result) -> { - if (result.succeeded()) { - callback.onResult(token, null); - } else { - callback.onResult(null, result.cause()); - } - }); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/EmailUtils.java b/src/main/java/net/frozenorb/apiv3/util/EmailUtils.java index 8ce8449..023f68b 100644 --- a/src/main/java/net/frozenorb/apiv3/util/EmailUtils.java +++ b/src/main/java/net/frozenorb/apiv3/util/EmailUtils.java @@ -1,48 +1,17 @@ package net.frozenorb.apiv3.util; -import com.google.common.collect.ImmutableSet; - -import net.frozenorb.apiv3.APIv3; - -import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; import lombok.experimental.UtilityClass; @UtilityClass -public class EmailUtils { +public final class EmailUtils { private static final Pattern VALID_EMAIL_PATTERN = Pattern.compile( - // this pattern is a slight modification (remove + as a valid char due to 'duplicate emails') of the standard one - "^[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", - Pattern.CASE_INSENSITIVE + // this pattern is a slight modification (remove + as a valid char due to 'duplicate emails') of the standard one + "^[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", + Pattern.CASE_INSENSITIVE ); - private static final HttpClient httpsClient = APIv3.getVertxInstance().createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true)); - private static Set bannedEmailDomains = ImmutableSet.of(); - - static { - APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(10), (id) -> updateBannedEmailDomains()); - } - - public static void updateBannedEmailDomains() { - httpsClient.get(443, "raw.githubusercontent.com", "/martenson/disposable-email-domains/master/disposable_email_blacklist.conf", (response) -> { - response.bodyHandler((body) -> bannedEmailDomains = ImmutableSet.copyOf(body.toString().split("\n"))); - response.exceptionHandler(Throwable::printStackTrace); - }).end(); - } - - public static boolean isBannedEmailDomain(String email) { - if (!isValidEmail(email)) { - return false; - } - - String[] split = email.split("@"); - String domain = split[1]; - return split.length == 2 && bannedEmailDomains.contains(domain.toLowerCase()); - } public static boolean isValidEmail(String email) { return email != null && VALID_EMAIL_PATTERN.matcher(email).matches(); diff --git a/src/main/java/net/frozenorb/apiv3/util/GeoJsonPoint.java b/src/main/java/net/frozenorb/apiv3/util/GeoJsonPoint.java index b82605d..a4106c3 100644 --- a/src/main/java/net/frozenorb/apiv3/util/GeoJsonPoint.java +++ b/src/main/java/net/frozenorb/apiv3/util/GeoJsonPoint.java @@ -1,6 +1,6 @@ package net.frozenorb.apiv3.util; -import net.frozenorb.apiv3.maxmind.MaxMindLocation; +import net.frozenorb.apiv3.geoip.GeoIpLocation; public final class GeoJsonPoint { @@ -9,8 +9,8 @@ public final class GeoJsonPoint { private GeoJsonPoint() {} // For Jackson - public GeoJsonPoint(MaxMindLocation maxMindLocation) { - this(maxMindLocation.getLongitude(), maxMindLocation.getLatitude()); + public GeoJsonPoint(GeoIpLocation geoIpLocation) { + this(geoIpLocation.getLongitude(), geoIpLocation.getLatitude()); } public GeoJsonPoint(double longitude, double latitude) { diff --git a/src/main/java/net/frozenorb/apiv3/util/MandrillUtils.java b/src/main/java/net/frozenorb/apiv3/util/MandrillUtils.java deleted file mode 100644 index c867d67..0000000 --- a/src/main/java/net/frozenorb/apiv3/util/MandrillUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.frozenorb.apiv3.util; - -import com.google.common.net.MediaType; - -import com.mongodb.async.SingleResultCallback; - -import net.frozenorb.apiv3.APIv3; - -import java.io.IOException; - -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpHeaders; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class MandrillUtils { - - private static final String mandrillApiKey = APIv3.getConfig().getProperty("mandrill.apiKey"); - private static final HttpClient httpClient = APIv3.getVertxInstance().createHttpClient(); - - public static void sendEmail(JsonObject message, SingleResultCallback callback) { - JsonObject requestBody = new JsonObject() - .put("key", mandrillApiKey) - .put("message", message); - - httpClient.post("mandrillapp.com", "/api/1.0/messages/send.json", (response) -> { - response.bodyHandler((responseBody) -> { - try { - JsonArray bodyJson = new JsonArray(responseBody.toString()); - JsonObject emailJson = bodyJson.getJsonObject(0); - String emailStatus = emailJson.getString("status"); - - if (emailStatus.equals("rejected") || emailStatus.equals("invalid")) { - callback.onResult(null, new IOException("Illegal email status while reading Mandrill response: " + emailStatus + " (" + emailJson.encode() + ")")); - } else { - callback.onResult(null, null); - } - } catch (Exception ex) { - callback.onResult(null, new IOException("Failed to process Mandrill response: " + responseBody, ex)); - } - }); - response.exceptionHandler((error) -> callback.onResult(null, error)); - }).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()).end(requestBody.encode()); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/MaxMindUtils.java b/src/main/java/net/frozenorb/apiv3/util/MaxMindUtils.java deleted file mode 100644 index 96fc014..0000000 --- a/src/main/java/net/frozenorb/apiv3/util/MaxMindUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.frozenorb.apiv3.util; - -import com.google.common.base.Charsets; - -import com.mongodb.async.SingleResultCallback; - -import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.maxmind.MaxMindResult; - -import java.util.Base64; - -import io.vertx.circuitbreaker.CircuitBreaker; -import io.vertx.circuitbreaker.CircuitBreakerOptions; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.json.JsonObject; -import lombok.experimental.UtilityClass; -import lombok.extern.slf4j.Slf4j; - -@UtilityClass -@Slf4j -public class MaxMindUtils { - - private static final String maxMindUserId = APIv3.getConfig().getProperty("maxMind.userId"); - private static final String maxMindLicenseKey = APIv3.getConfig().getProperty("maxMind.maxMindLicenseKey"); - private static final HttpClient httpsClient = APIv3.getVertxInstance().createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true)); - private static final CircuitBreaker breaker; - - // MaxMind likes to randomly not respond, so we take advantage of the circuit breaker pattern to only - // check MaxMind periodically (while it's in a non-responsive state) to keep our average response times - // nice and low. - static { - breaker = CircuitBreaker.create("maxmind-circuit-breaker", APIv3.getVertxInstance(), - new CircuitBreakerOptions() - .setMaxFailures(5) - .setTimeout(5000) // 5 secondS - .setFallbackOnFailure(true) - .setResetTimeout(120_000) // 2 minutes - ); - breaker.fallback((ignored) -> null); - breaker.openHandler((ignored) -> log.info("MaxMind circuit breaker is now open.")); - breaker.halfOpenHandler((ignored) -> log.info("MaxMind circuit breaker is now half-open.")); - breaker.closeHandler((ignored) -> log.info("MaxMind circuit breaker is now closed.")); - } - - public static void getInsights(String ip, SingleResultCallback callback) { - breaker.execute((future) -> { - String authHeader = "Basic " + Base64.getEncoder().encodeToString((maxMindUserId + ":" + maxMindLicenseKey).getBytes(Charsets.UTF_8)); - - httpsClient.get(443, "geoip.maxmind.com", "/geoip/v2.1/insights/" + ip, (response) -> { - response.bodyHandler((body) -> { - JsonObject bodyJson = new JsonObject(body.toString()); - - try { - MaxMindResult maxMindResult = new MaxMindResult(bodyJson); - - // we have to check !isComplete() because the circuit breaker's timeout might mark us as failed already - if (!future.isComplete()) { - future.complete(maxMindResult); - } - } catch (Exception ignored) { - // we have to check !isComplete() because the circuit breaker's timeout might mark us as failed already - if (!future.isComplete()) { - future.complete(null); - } - } - }); - - response.exceptionHandler((error) -> { - // we have to check !isComplete() because the circuit breaker's timeout will might us as failed already - if (!future.isComplete()) { - future.fail(error); - } - }); - }).putHeader("Authorization", authHeader).end(); - }).setHandler((result) -> { - if (result.failed()) { - callback.onResult(null, result.cause()); - } else { - callback.onResult((MaxMindResult) result.result(), null); - } - }); - } - - public static String getEnglishName(JsonObject source) { - return source.getJsonObject("names", new JsonObject()).getString("en", "INVALID"); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/MojangUtils.java b/src/main/java/net/frozenorb/apiv3/util/MojangUtils.java deleted file mode 100644 index a083f74..0000000 --- a/src/main/java/net/frozenorb/apiv3/util/MojangUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.frozenorb.apiv3.util; - -import com.mongodb.async.SingleResultCallback; - -import net.frozenorb.apiv3.APIv3; - -import java.io.IOException; -import java.util.UUID; - -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.json.DecodeException; -import io.vertx.core.json.JsonObject; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class MojangUtils { - - private static final HttpClient httpsClient = APIv3.getVertxInstance().createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true)); - - public static void getName(UUID id, SingleResultCallback callback) { - httpsClient.get(443, "sessionserver.mojang.com", "/session/minecraft/profile/" + id.toString().replace("-", ""), (response) -> { - response.bodyHandler((body) -> { - try { - JsonObject bodyJson = new JsonObject(body.toString()); - String name = bodyJson.getString("name"); - - if (name == null) { - callback.onResult(null, new IOException("Hit Mojang API rate limit: " + bodyJson.encode())); - } else { - callback.onResult(name, null); - } - } catch (DecodeException ex) { - callback.onResult(null, ex); - } - }); - - response.exceptionHandler((error) -> callback.onResult(null, error)); - }).end(); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/SpringUtils.java b/src/main/java/net/frozenorb/apiv3/util/SpringUtils.java new file mode 100644 index 0000000..d4fd69b --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/util/SpringUtils.java @@ -0,0 +1,16 @@ +package net.frozenorb.apiv3.util; + +import org.springframework.beans.factory.BeanFactory; + +import lombok.Getter; +import lombok.Setter; + +public final class SpringUtils { + + @Getter @Setter private static BeanFactory beanFactory; + + public static T getBean(Class type) { + return beanFactory.getBean(type); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/SyncUtils.java b/src/main/java/net/frozenorb/apiv3/util/SyncUtils.java index 8eef80d..ed83c28 100644 --- a/src/main/java/net/frozenorb/apiv3/util/SyncUtils.java +++ b/src/main/java/net/frozenorb/apiv3/util/SyncUtils.java @@ -4,18 +4,17 @@ import com.google.common.util.concurrent.SettableFuture; import com.mongodb.async.SingleResultCallback; -import net.frozenorb.apiv3.APIv3; - import java.util.concurrent.ExecutionException; import io.vertx.core.Context; +import io.vertx.core.Vertx; import lombok.experimental.UtilityClass; @UtilityClass public class SyncUtils { public static SingleResultCallback vertxWrap(SingleResultCallback callback) { - Context context = APIv3.getVertxInstance().getOrCreateContext(); + Context context = SpringUtils.getBean(Vertx.class).getOrCreateContext(); return (result, error) -> { context.runOnContext((ignored) -> { diff --git a/src/main/java/net/frozenorb/apiv3/util/TotpUtils.java b/src/main/java/net/frozenorb/apiv3/util/TotpUtils.java deleted file mode 100644 index 7db8a38..0000000 --- a/src/main/java/net/frozenorb/apiv3/util/TotpUtils.java +++ /dev/null @@ -1,84 +0,0 @@ -package net.frozenorb.apiv3.util; - -import com.mongodb.async.SingleResultCallback; -import com.warrenstrange.googleauth.GoogleAuthenticator; -import com.warrenstrange.googleauth.GoogleAuthenticatorConfig; - -import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.model.User; - -import java.util.concurrent.TimeUnit; - -import io.vertx.redis.RedisClient; -import io.vertx.redis.RedisOptions; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class TotpUtils { - - private static final GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build()); - private static final RedisClient redisClient = RedisClient.create(APIv3.getVertxInstance(), - new RedisOptions() - .setAddress(APIv3.getConfig().getProperty("redis.address")) - .setPort(Integer.parseInt(APIv3.getConfig().getProperty("redis.port"))) - ); - - public static boolean authorizeUser(String secret, int code) { - return googleAuthenticator.authorize(secret, code); - } - - public static void isPreAuthorized(User user, String ip, SingleResultCallback callback) { - if (!IpUtils.isValidIp(ip)) { - callback.onResult(false, null); - return; - } - - redisClient.exists(user.getId() + ":preAuthorizedIp:" + ip.toLowerCase(), (result) -> { - if (result.succeeded()) { - callback.onResult(result.result() == 1, null); - } else { - callback.onResult(null, result.cause()); - } - }); - } - - public static void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback callback) { - if (!IpUtils.isValidIp(ip)) { - callback.onResult(null, null); - return; - } - - String key = user.getId() + ":preAuthorizedIp:" + ip.toLowerCase(); - - redisClient.setex(key, unit.toSeconds(duration), "", (result) -> { - if (result.succeeded()) { - callback.onResult(null, null); - } else { - callback.onResult(null, result.cause()); - } - }); - } - - public static void wasRecentlyUsed(User user, int code, SingleResultCallback callback) { - redisClient.exists(user.getId() + ":recentTotpCodes:" + code, (result) -> { - if (result.succeeded()) { - callback.onResult(result.result() == 1, null); - } else { - callback.onResult(null, result.cause()); - } - }); - } - - public static void markRecentlyUsed(User user, int code, SingleResultCallback callback) { - String key = user.getId() + ":recentTotpCodes:" + code; - - redisClient.setex(key, TimeUnit.MINUTES.toSeconds(5), "", (result) -> { - if (result.succeeded()) { - callback.onResult(null, null); - } else { - callback.onResult(null, result.cause()); - } - }); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/UserSessionUtils.java b/src/main/java/net/frozenorb/apiv3/util/UserSessionUtils.java deleted file mode 100644 index 0b992ec..0000000 --- a/src/main/java/net/frozenorb/apiv3/util/UserSessionUtils.java +++ /dev/null @@ -1,92 +0,0 @@ -package net.frozenorb.apiv3.util; - -import com.mongodb.async.SingleResultCallback; - -import net.frozenorb.apiv3.APIv3; - -import java.util.List; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import io.vertx.redis.RedisClient; -import io.vertx.redis.RedisOptions; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class UserSessionUtils { - - private static final RedisClient redisClient = RedisClient.create(APIv3.getVertxInstance(), - new RedisOptions() - .setAddress(APIv3.getConfig().getProperty("redis.address")) - .setPort(Integer.parseInt(APIv3.getConfig().getProperty("redis.port"))) - ); - - public static void sessionExists(String userIp, String userSession, SingleResultCallback callback) { - if (userIp == null || userIp.isEmpty() || userSession == null || userSession.isEmpty()) { - callback.onResult(false, null); - return; - } - - redisClient.exists("apiv3:sessions:" + userIp + ":" + userSession, (result) -> { - if (result.succeeded()) { - callback.onResult(result.result() == 1, null); - } else { - callback.onResult(null, result.cause()); - } - }); - } - - public static void createSession(UUID user, String userIp, SingleResultCallback callback) { - String userSession = UUID.randomUUID().toString().replaceAll("-", ""); - String key = "apiv3:sessions:" + userIp + ":" + userSession; - - redisClient.setex(key, TimeUnit.DAYS.toSeconds(30), "", (result) -> { - if (result.succeeded()) { - redisClient.sadd("apiv3:sessions:" + user, key, (result2) -> { - if (result2.succeeded()) { - callback.onResult(userSession, null); - } else { - callback.onResult(null, result2.cause()); - } - }); - } else { - callback.onResult(null, result.cause()); - } - }); - } - - public static void invalidateSession(String userIp, String userSession, SingleResultCallback callback) { - redisClient.del("apiv3:sessions:" + userIp + ":" + userSession, (result) -> { - if (result.succeeded()) { - callback.onResult(null, null); - } else { - callback.onResult(null, result.cause()); - } - }); - } - - public static void invalidateAllSessions(UUID user, SingleResultCallback callback) { - redisClient.smembers("apiv3:sessions:" + user, (result) -> { - if (result.failed()) { - callback.onResult(null, result.cause()); - return; - } - - List sessions = (List) result.result().getList(); - - if (sessions.isEmpty()) { - callback.onResult(null, null); - return; - } - - redisClient.delMany(sessions, (result2) -> { - if (result2.succeeded()) { - callback.onResult(null, null); - } else { - callback.onResult(null, result2.cause()); - } - }); - }); - } - -} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/util/ZangUtils.java b/src/main/java/net/frozenorb/apiv3/util/ZangUtils.java deleted file mode 100644 index bb68fd8..0000000 --- a/src/main/java/net/frozenorb/apiv3/util/ZangUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -package net.frozenorb.apiv3.util; - -import com.google.common.base.Charsets; -import com.google.common.net.MediaType; - -import com.mongodb.async.SingleResultCallback; - -import net.frozenorb.apiv3.APIv3; -import net.frozenorb.apiv3.zang.ZangResult; - -import java.io.IOException; -import java.util.Base64; - -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpHeaders; -import io.vertx.core.json.JsonObject; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class ZangUtils { - - private static final String zangAccountSid = APIv3.getConfig().getProperty("zang.accountSid"); - private static final String zangAuthToken = APIv3.getConfig().getProperty("zang.authToken"); - private static final HttpClient httpsClient = APIv3.getVertxInstance().createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true)); - - public static void sendText(String to, String messageBody, SingleResultCallback callback) { - String authHeader = "Basic " + Base64.getEncoder().encodeToString((zangAccountSid + ":" + zangAuthToken).getBytes(Charsets.UTF_8)); - - httpsClient.post(443, "api.zang.io", "/v2/Accounts/" + zangAccountSid + "/SMS/Messages.json", (response) -> { - response.bodyHandler((body) -> { - JsonObject bodyJson = new JsonObject(body.toString()); - - if (bodyJson.getString("status", "").equals("queued")) { - callback.onResult(null, null); - } else { - callback.onResult(null, new IOException("Could not send text message: " + bodyJson.encode())); - } - }); - - response.exceptionHandler((error) -> callback.onResult(null, error)); - }).putHeader("Authorization", authHeader).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()).end("To=" + to + "&From=339-337-5300&Body=" + messageBody); - } - - public static void getCarrierInfo(String phoneNumber, SingleResultCallback callback) { - String authHeader = "Basic " + Base64.getEncoder().encodeToString((zangAccountSid + ":" + zangAuthToken).getBytes(Charsets.UTF_8)); - - httpsClient.post(443, "api.zang.io", "/v2/Accounts/" + zangAccountSid + "/Lookups/Carrier.json", (response) -> { - response.bodyHandler((body) -> { - JsonObject bodyJson = new JsonObject(body.toString()); - - if (bodyJson.containsKey("carrier_lookups")) { - // Zang returns an array, but we don't batch at all so we always just get the first element - JsonObject lookupResult = bodyJson.getJsonArray("carrier_lookups").getJsonObject(0); - callback.onResult(new ZangResult(lookupResult), null); - } else { - callback.onResult(null, new IOException("Could not parse Zang result: " + bodyJson.encode())); - } - }); - - response.exceptionHandler((error) -> callback.onResult(null, error)); - }) - .putHeader("Authorization", authHeader) - .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()) - .end("PhoneNumber=" + phoneNumber); - } - -} \ No newline at end of file