Merge pull request #11 from FrozenOrb/experimental
Use Vertx instead of Spark
This commit is contained in:
commit
2cab2f0e7a
@ -1,6 +1,4 @@
|
||||
general.releaseStage=production
|
||||
logging.level=info
|
||||
logging.debug=true
|
||||
mongo.address=localhost
|
||||
mongo.port=27017
|
||||
mongo.database=MineHQ
|
||||
@ -8,13 +6,8 @@ mongo.username=
|
||||
mongo.password=
|
||||
redis.address=localhost
|
||||
redis.port=6379
|
||||
http.address=0.0.0.0
|
||||
http.port=80
|
||||
http.workerThreads=
|
||||
twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740
|
||||
twillio.authToken=982592505a171d3be6b0722f5ecacc0e
|
||||
mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ
|
||||
bugsnag.apiKey=0e47fba8b825416b7cbc839066184509
|
||||
auth.permittedUserRanks=developer,owner
|
||||
auth.websiteApiKey=RVbp4hY6sCFVaf
|
||||
auth.bungeeCordApiKey=6d9cf76dc9f0d23
|
||||
auth.bungeeApiKey=6d9cf76dc9f0d23
|
115
pom.xml
115
pom.xml
@ -19,6 +19,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
@ -33,9 +34,9 @@
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>*:*</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>org.projectlombok:lombok</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
<executions>
|
||||
@ -55,29 +56,67 @@
|
||||
<id>minehq-repo</id>
|
||||
<url>http://maven.minehq.com:8081/artifactory/minehq-all/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>mongo-jackson-codec-repo</id>
|
||||
<url>https://dl.bintray.com/ylemoigne/maven</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Vert.x -->
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.5</version>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-core</artifactId>
|
||||
<version>3.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-web</artifactId>
|
||||
<version>3.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-redis-client</artifactId>
|
||||
<version>3.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Libs -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.6.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Mongo -->
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongodb-driver-async</artifactId>
|
||||
<version>3.0.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fr.javatic.mongo</groupId>
|
||||
<artifactId>mongo-jackson-codec</artifactId>
|
||||
<version>3.2.0__0.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.undercouch</groupId>
|
||||
<artifactId>bson4jackson</artifactId>
|
||||
<version>2.6.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TOTP -->
|
||||
<dependency>
|
||||
<groupId>com.warrenstrange</groupId>
|
||||
<artifactId>googleauth</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Monitoring -->
|
||||
<dependency>
|
||||
<groupId>com.indeed</groupId>
|
||||
<artifactId>java-dogstatsd-client</artifactId>
|
||||
@ -88,61 +127,15 @@
|
||||
<artifactId>bugsnag</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<version>3.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cribbstechnologies.clients</groupId>
|
||||
<artifactId>mandrillClient</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twilio.sdk</groupId>
|
||||
<artifactId>twilio-java-sdk</artifactId>
|
||||
<version>6.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mindrot</groupId>
|
||||
<artifactId>jbcrypt</artifactId>
|
||||
<version>0.3m</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb.morphia</groupId>
|
||||
<artifactId>morphia</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb.morphia</groupId>
|
||||
<artifactId>morphia-logging-slf4j</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.6.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.warrenstrange</groupId>
|
||||
<artifactId>googleauth</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
<artifactId>okio</artifactId>
|
||||
<version>1.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@ -1,154 +1,217 @@
|
||||
package net.frozenorb.apiv3;
|
||||
|
||||
import com.bugsnag.Client;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.mongodb.*;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.timgroup.statsd.NonBlockingStatsDClient;
|
||||
import com.timgroup.statsd.StatsDClient;
|
||||
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 fr.javatic.mongo.jacksonCodec.JacksonCodecProvider;
|
||||
import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory;
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.http.HttpClient;
|
||||
import io.vertx.core.http.HttpHeaders;
|
||||
import io.vertx.core.http.HttpMethod;
|
||||
import io.vertx.core.http.HttpServer;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
import io.vertx.ext.web.handler.LoggerFormat;
|
||||
import io.vertx.ext.web.handler.LoggerHandler;
|
||||
import io.vertx.redis.RedisClient;
|
||||
import io.vertx.redis.RedisOptions;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.actors.ActorType;
|
||||
import net.frozenorb.apiv3.filters.*;
|
||||
import net.frozenorb.apiv3.models.*;
|
||||
import net.frozenorb.apiv3.routes.GETDump;
|
||||
import net.frozenorb.apiv3.routes.GETWhoAmI;
|
||||
import net.frozenorb.apiv3.routes.NotFound;
|
||||
import net.frozenorb.apiv3.routes.POSTMetrics;
|
||||
import net.frozenorb.apiv3.routes.announcements.GETAnnouncements;
|
||||
import net.frozenorb.apiv3.routes.auditLog.GETAuditLog;
|
||||
import net.frozenorb.apiv3.routes.chatFilterList.GETChatFilterList;
|
||||
import net.frozenorb.apiv3.routes.grants.*;
|
||||
import net.frozenorb.apiv3.routes.ipLog.GETUserIPLog;
|
||||
import net.frozenorb.apiv3.routes.notificationTemplate.DELETENotificationTemplate;
|
||||
import net.frozenorb.apiv3.routes.notificationTemplate.GETNotificationTemplate;
|
||||
import net.frozenorb.apiv3.routes.notificationTemplate.GETNotificationTemplates;
|
||||
import net.frozenorb.apiv3.routes.notificationTemplate.POSTNotificationTemplate;
|
||||
import net.frozenorb.apiv3.routes.punishments.*;
|
||||
import net.frozenorb.apiv3.routes.ranks.DELETERank;
|
||||
import net.frozenorb.apiv3.routes.ranks.GETRank;
|
||||
import net.frozenorb.apiv3.routes.ranks.GETRanks;
|
||||
import net.frozenorb.apiv3.routes.ranks.POSTRank;
|
||||
import net.frozenorb.apiv3.routes.serverGroups.DELETEServerGroup;
|
||||
import net.frozenorb.apiv3.routes.serverGroups.GETServerGroup;
|
||||
import net.frozenorb.apiv3.routes.serverGroups.GETServerGroups;
|
||||
import net.frozenorb.apiv3.routes.serverGroups.POSTServerGroup;
|
||||
import net.frozenorb.apiv3.routes.servers.*;
|
||||
import net.frozenorb.apiv3.routes.users.*;
|
||||
import net.frozenorb.apiv3.serialization.DateTypeAdapter;
|
||||
import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy;
|
||||
import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter;
|
||||
import net.frozenorb.apiv3.handler.ActorAttributeHandler;
|
||||
import net.frozenorb.apiv3.handler.AuthorizationHandler;
|
||||
import net.frozenorb.apiv3.route.GETDump;
|
||||
import net.frozenorb.apiv3.route.GETWhoAmI;
|
||||
import net.frozenorb.apiv3.route.POSTMetrics;
|
||||
import net.frozenorb.apiv3.route.announcements.GETAnnouncements;
|
||||
import net.frozenorb.apiv3.route.announcements.PUTAnnouncements;
|
||||
import net.frozenorb.apiv3.route.auditLog.GETAuditLog;
|
||||
import net.frozenorb.apiv3.route.auditLog.POSTUserAuditLogEntry;
|
||||
import net.frozenorb.apiv3.route.chatFilterList.GETChatFilterList;
|
||||
import net.frozenorb.apiv3.route.grants.*;
|
||||
import net.frozenorb.apiv3.route.ipBans.*;
|
||||
import net.frozenorb.apiv3.route.ipLog.GETUserIpLog;
|
||||
import net.frozenorb.apiv3.route.notificationTemplates.DELETENotificationTemplate;
|
||||
import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplate;
|
||||
import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplates;
|
||||
import net.frozenorb.apiv3.route.notificationTemplates.POSTNotificationTemplate;
|
||||
import net.frozenorb.apiv3.route.punishments.*;
|
||||
import net.frozenorb.apiv3.route.ranks.DELETERank;
|
||||
import net.frozenorb.apiv3.route.ranks.GETRank;
|
||||
import net.frozenorb.apiv3.route.ranks.GETRanks;
|
||||
import net.frozenorb.apiv3.route.ranks.POSTRank;
|
||||
import net.frozenorb.apiv3.route.serverGroups.DELETEServerGroup;
|
||||
import net.frozenorb.apiv3.route.serverGroups.GETServerGroup;
|
||||
import net.frozenorb.apiv3.route.serverGroups.GETServerGroups;
|
||||
import net.frozenorb.apiv3.route.serverGroups.POSTServerGroup;
|
||||
import net.frozenorb.apiv3.route.servers.*;
|
||||
import net.frozenorb.apiv3.route.users.*;
|
||||
import net.frozenorb.apiv3.serialization.gson.DateTypeAdapter;
|
||||
import net.frozenorb.apiv3.serialization.gson.FollowAnnotationExclusionStrategy;
|
||||
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonDeserializer;
|
||||
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonSerializer;
|
||||
import net.frozenorb.apiv3.serialization.mongodb.UUIDCodecProvider;
|
||||
import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger;
|
||||
import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler;
|
||||
import net.frozenorb.apiv3.utils.IPUtils;
|
||||
import net.frozenorb.apiv3.utils.PermissionUtils;
|
||||
import net.frozenorb.apiv3.utils.UUIDUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.Datastore;
|
||||
import org.mongodb.morphia.Morphia;
|
||||
import org.mongodb.morphia.converters.UUIDConverter;
|
||||
import org.mongodb.morphia.logging.MorphiaLoggerFactory;
|
||||
import org.mongodb.morphia.logging.slf4j.SLF4JLoggerImplFactory;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import org.bson.codecs.BsonValueCodecProvider;
|
||||
import org.bson.codecs.DocumentCodecProvider;
|
||||
import org.bson.codecs.ValueCodecProvider;
|
||||
import org.bson.codecs.configuration.CodecProvider;
|
||||
import org.bson.codecs.configuration.CodecRegistries;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static spark.Spark.*;
|
||||
import static spark.route.RouteOverview.enableRouteOverview;
|
||||
|
||||
@Slf4j
|
||||
public final class APIv3 {
|
||||
public final class APIv3 extends AbstractVerticle {
|
||||
|
||||
@Getter private static Datastore datastore;
|
||||
@Getter private static HttpClient httpClient;
|
||||
@Getter private static MongoDatabase database;
|
||||
@Getter private static Properties config = new Properties();
|
||||
@Getter private static JedisPool redisPool;
|
||||
@Getter private static StatsDClient statsD;
|
||||
@Getter private static final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter())
|
||||
@Getter private static RedisClient redisClient;
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Date.class, new DateTypeAdapter())
|
||||
.setExclusionStrategies(new FollowAnnotationExclusionStrategy())
|
||||
.create();
|
||||
|
||||
APIv3() {
|
||||
@Override
|
||||
public void start() {
|
||||
setupConfig();
|
||||
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", config.getProperty("logging.level"));
|
||||
|
||||
setupDatabase();
|
||||
setupRedis();
|
||||
setupMetrics();
|
||||
setupBugsnag();
|
||||
setupHttp();
|
||||
setupHttpServer();
|
||||
setupHttpClient();
|
||||
|
||||
//convertData("158.69.126.126", true);
|
||||
LoggingFilter.setDebug(Boolean.valueOf(config.getProperty("logging.debug")));
|
||||
/*V2Importer converter = new V2Importer("mongodb://158.69.126.126", "minehq");
|
||||
|
||||
converter.startImport((ignored, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
private void setupConfig() {
|
||||
try (InputStream in = new FileInputStream("apiv3.properties")) {
|
||||
config.load(in);
|
||||
} catch (Exception ex) {
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupDatabase() {
|
||||
ImmutableList<MongoCredential> credentials = ImmutableList.of();
|
||||
List<MongoCredential> credentials = ImmutableList.of();
|
||||
|
||||
if (!config.getProperty("mongo.username").isEmpty()) {
|
||||
credentials = ImmutableList.of(MongoCredential.createCredential(
|
||||
credentials = ImmutableList.of(
|
||||
MongoCredential.createCredential(
|
||||
config.getProperty("mongo.username"),
|
||||
config.getProperty("mongo.database"),
|
||||
config.getProperty("mongo.password").toCharArray()
|
||||
));
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
MongoClient mongoClient = new MongoClient(new ServerAddress(
|
||||
config.getProperty("mongo.address"),
|
||||
Integer.parseInt(config.getProperty("mongo.port"))),
|
||||
credentials
|
||||
ConnectionString connectionString = new ConnectionString("mongodb://" + config.getProperty("mongo.address") + ":" + config.getProperty("mongo.port"));
|
||||
|
||||
MongoClient mongoClient = MongoClients.create(MongoClientSettings
|
||||
.builder()
|
||||
.codecRegistry(createCodecRegistry())
|
||||
.credentialList(credentials)
|
||||
.clusterSettings(ClusterSettings.builder()
|
||||
.applyConnectionString(connectionString)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
);
|
||||
|
||||
MorphiaLoggerFactory.reset();
|
||||
MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class);
|
||||
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))
|
||||
), (a, b) -> {});
|
||||
database.getCollection("punishments").createIndexes(ImmutableList.of(
|
||||
new IndexModel(new Document("user", 1)),
|
||||
new IndexModel(new Document("type", 1)),
|
||||
new IndexModel(new Document("addedAt", 1)),
|
||||
new IndexModel(new Document("addedBy", 1))
|
||||
), (a, b) -> {});
|
||||
database.getCollection("users").createIndexes(ImmutableList.of(
|
||||
new IndexModel(new Document("lastUsername", 1)),
|
||||
new IndexModel(new Document("emailToken", 1))
|
||||
), (a, b) -> {});
|
||||
database.getCollection("userMeta").createIndexes(ImmutableList.of(
|
||||
new IndexModel(new Document("user", 1).append("serverGroup", 1))
|
||||
), (a, b) -> {});
|
||||
}
|
||||
|
||||
Morphia morphia = new Morphia();
|
||||
morphia.mapPackage("net.frozenorb.apiv3.models");
|
||||
morphia.getMapper().getConverters().addConverter(new UUIDConverter());
|
||||
private CodecRegistry createCodecRegistry() {
|
||||
ObjectMapper objectMapper = ObjectMapperFactory.createObjectMapper();
|
||||
SimpleModule simpleModule = new SimpleModule();
|
||||
|
||||
datastore = morphia.createDatastore(mongoClient, config.getProperty("mongo.database"));
|
||||
datastore.ensureIndexes();
|
||||
simpleModule.addSerializer(UUID.class, new UUIDJsonSerializer());
|
||||
simpleModule.addDeserializer(UUID.class, new UUIDJsonDeserializer());
|
||||
|
||||
objectMapper.registerModule(simpleModule);
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
|
||||
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
List<CodecProvider> providers = new ArrayList<>();
|
||||
|
||||
// Our fixed uuid codec
|
||||
providers.add(new UUIDCodecProvider());
|
||||
|
||||
// Normal providers
|
||||
providers.add(new ValueCodecProvider());
|
||||
providers.add(new DocumentCodecProvider());
|
||||
providers.add(new BsonValueCodecProvider());
|
||||
|
||||
// Jackson parser codec
|
||||
providers.add(new JacksonCodecProvider(objectMapper));
|
||||
|
||||
return CodecRegistries.fromProviders(providers);
|
||||
}
|
||||
|
||||
private void setupRedis() {
|
||||
redisPool = new JedisPool(
|
||||
config.getProperty("redis.address"),
|
||||
Integer.parseInt(config.getProperty("redis.port"))
|
||||
redisClient = RedisClient.create(
|
||||
vertx,
|
||||
new RedisOptions()
|
||||
.setAddress(config.getProperty("redis.address"))
|
||||
.setPort(Integer.parseInt(config.getProperty("redis.port")))
|
||||
);
|
||||
}
|
||||
|
||||
private void setupMetrics() {
|
||||
statsD = new NonBlockingStatsDClient(null, "localhost", 8125);
|
||||
|
||||
new Timer("Metrics Task").scheduleAtFixedRate(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
statsD.recordGaugeValue("apiv3.memory.usage", Runtime.getRuntime().totalMemory() / (1024 * 1024));
|
||||
statsD.recordGaugeValue("apiv3.memory.max", Runtime.getRuntime().maxMemory() / (1024 * 1024));
|
||||
}
|
||||
|
||||
}, TimeUnit.SECONDS.toMillis(5), TimeUnit.SECONDS.toMillis(5));
|
||||
}
|
||||
|
||||
private void setupBugsnag() {
|
||||
Client bugsnag = new Client(config.getProperty("bugsnag.apiKey"));
|
||||
bugsnag.setReleaseStage(config.getProperty("general.releaseStage"));
|
||||
@ -156,272 +219,108 @@ public final class APIv3 {
|
||||
bugsnag.setLogger(new BugsnagSLF4JLogger());
|
||||
}
|
||||
|
||||
private void setupHttp() {
|
||||
ipAddress(config.getProperty("http.address"));
|
||||
port(Integer.parseInt(config.getProperty("http.port")));
|
||||
String workerThreads = config.getProperty("http.workerThreads");
|
||||
// TODO: blockingHandler -> handler
|
||||
private void setupHttpServer() {
|
||||
HttpServer webServer = vertx.createHttpServer();
|
||||
Router mainRouter = Router.router(vertx);
|
||||
|
||||
if (!workerThreads.isEmpty()) {
|
||||
threadPool(Integer.parseInt(workerThreads));
|
||||
}
|
||||
|
||||
before(new MetricsBeforeFilter());
|
||||
before(new ContentTypeFilter());
|
||||
before(new ActorAttributeFilter());
|
||||
before(new AuthorizationFilter());
|
||||
after(new MetricsAfterFilter());
|
||||
after(new LoggingFilter());
|
||||
exception(Exception.class, new LoggingExceptionHandler());
|
||||
mainRouter.route().handler(new ActorAttributeHandler());
|
||||
mainRouter.route().handler(new AuthorizationHandler());
|
||||
mainRouter.route().handler(LoggerHandler.create(LoggerFormat.TINY));
|
||||
mainRouter.route().method(HttpMethod.PUT).method(HttpMethod.POST).handler(BodyHandler.create());
|
||||
|
||||
// TODO: The commented out routes
|
||||
|
||||
post("/metrics", new POSTMetrics(), gson::toJson);
|
||||
get("/announcements", new GETAnnouncements(), gson::toJson);
|
||||
get("/auditLog", new GETAuditLog(), gson::toJson);
|
||||
get("/chatFilterList", new GETChatFilterList(), gson::toJson);
|
||||
get("/dump/:type", new GETDump(), gson::toJson);
|
||||
get("/whoami", new GETWhoAmI(), gson::toJson);
|
||||
enableRouteOverview("/routes");
|
||||
mainRouter.get("/announcements/:id").blockingHandler(new GETAnnouncements());
|
||||
mainRouter.put("/announcements/:id").blockingHandler(new PUTAnnouncements());
|
||||
|
||||
get("/grant/:id", new GETGrant(), gson::toJson);
|
||||
get("/grants", new GETGrants(), gson::toJson);
|
||||
delete("/grant/:id", new DELETEGrant(), gson::toJson);
|
||||
mainRouter.get("/auditLog").blockingHandler(new GETAuditLog());
|
||||
mainRouter.post("/user/:id/auditLogEntry").blockingHandler(new POSTUserAuditLogEntry());
|
||||
|
||||
get("/notificationTemplate/:id", new GETNotificationTemplate(), gson::toJson);
|
||||
get("/notificationTemplates", new GETNotificationTemplates(), gson::toJson);
|
||||
post("/notificationTemplate", new POSTNotificationTemplate(), gson::toJson);
|
||||
//put("/notificationTemplate/:id", new PUTNotificationTemplate(), gson::toJson);
|
||||
delete("/notificationTemplate/:id", new DELETENotificationTemplate(), gson::toJson);
|
||||
mainRouter.get("/chatFilterList").handler(new GETChatFilterList());
|
||||
|
||||
get("/punishment/:id", new GETPunishment(), gson::toJson);
|
||||
get("/punishments", new GETPunishments(), gson::toJson);
|
||||
delete("/punishment/:id", new DELETEPunishment(), gson::toJson);
|
||||
mainRouter.get("/grant/:id").blockingHandler(new GETGrant());
|
||||
mainRouter.get("/grants").blockingHandler(new GETGrants());
|
||||
mainRouter.get("/user/:id/grants").blockingHandler(new GETUserGrants());
|
||||
mainRouter.post("/user/:id/grant").blockingHandler(new POSTUserGrant());
|
||||
mainRouter.delete("/grant/:id").blockingHandler(new DELETEGrant());
|
||||
|
||||
get("/rank/:id", new GETRank(), gson::toJson);
|
||||
get("/ranks", new GETRanks(), gson::toJson);
|
||||
post("/rank", new POSTRank(), gson::toJson);
|
||||
//put("/rank/:id", new PUTRank(), gson::toJson);
|
||||
delete("/rank/:id", new DELETERank(), gson::toJson);
|
||||
mainRouter.get("/ipBan/:id").blockingHandler(new GETIpBan());
|
||||
mainRouter.get("/ipBans").blockingHandler(new GETIpBans());
|
||||
mainRouter.get("/ip/:id/ipBans").blockingHandler(new GETIpIpBans());
|
||||
mainRouter.post("/ip/:id/ipBan").blockingHandler(new POSTIpIpBan());
|
||||
mainRouter.delete("/ipBan/:id").blockingHandler(new DELETEIpBan());
|
||||
|
||||
get("/serverGroup/:id", new GETServerGroup(), gson::toJson);
|
||||
get("/serverGroups", new GETServerGroups(), gson::toJson);
|
||||
post("/serverGroup", new POSTServerGroup(), gson::toJson);
|
||||
//put("/serverGroup/:id", new PUTServerGroup(), gson::toJson);
|
||||
delete("/serverGroup/:id", new DELETEServerGroup(), gson::toJson);
|
||||
mainRouter.get("/user/:id/ipLog").blockingHandler(new GETUserIpLog());
|
||||
|
||||
get("/server/:id", new GETServer(), gson::toJson);
|
||||
get("/servers", new GETServers(), gson::toJson);
|
||||
post("/server/heartbeat", new POSTServerHeartbeat(), gson::toJson);
|
||||
post("/server", new POSTServer(), gson::toJson);
|
||||
//put("/server/:id", new PUTServer(), gson::toJson);
|
||||
delete("/server/:id", new DELETEServer(), gson::toJson);
|
||||
mainRouter.get("/notificationTemplate/:id").blockingHandler(new GETNotificationTemplate());
|
||||
mainRouter.get("/notificationTemplates").blockingHandler(new GETNotificationTemplates());
|
||||
mainRouter.post("/notificationTemplate").blockingHandler(new POSTNotificationTemplate());
|
||||
//mainRouter.put("/notificationTemplate/:id").blockingHandler(new PUTNotificationTemplate());
|
||||
mainRouter.delete("/notificationTemplate/:id").blockingHandler(new DELETENotificationTemplate());
|
||||
|
||||
get("/staff", new GETStaff(), gson::toJson);
|
||||
get("/user/:id/details", new GETUserDetails(), gson::toJson);
|
||||
get("/user/:id/meta/:serverGroup", new GETUserMeta(), gson::toJson);
|
||||
get("/user/:id/grants", new GETUserGrants(), gson::toJson);
|
||||
get("/user/:id/punishments", new GETUserPunishments(), gson::toJson);
|
||||
get("/user/:id/ipLog", new GETUserIPLog(), gson::toJson);
|
||||
get("/user/:id/requiresTOTP", new GETUserRequiresTOTP(), gson::toJson);
|
||||
get("/user/:id/verifyPassword", new GETUserVerifyPassword(), gson::toJson);
|
||||
get("/user/:id", new GETUser(), gson::toJson);
|
||||
post("/user/:id/verifyTOTP", new POSTUserVerifyTOTP(), gson::toJson);
|
||||
post("/user/:id/grant", new POSTUserGrant(), gson::toJson);
|
||||
post("/user/:id/punish", new POSTUserPunish(), gson::toJson);
|
||||
post("/user/:id/login", new POSTUserLogin(), gson::toJson);
|
||||
post("/user/:id/leave", new POSTUserLeave(), gson::toJson);
|
||||
post("/user/:id/notify", new POSTUserNotify(), gson::toJson);
|
||||
post("/user/:id/register", new POSTUserRegister(), gson::toJson);
|
||||
post("/user/:id/setupTOTP", new POSTUserSetupTOTP(), gson::toJson);
|
||||
post("/user/confirmRegister/:emailToken", new POSTUserConfirmRegister(), gson::toJson);
|
||||
put("/user/:id/meta/:serverGroup", new PUTUserMeta(), gson::toJson);
|
||||
delete("/user/:id/meta/:serverGroup", new DELETEUserMeta(), gson::toJson);
|
||||
delete("/user/:id/punishment", new DELETEUserPunishment(), gson::toJson);
|
||||
mainRouter.get("/punishment/:id").blockingHandler(new GETPunishment());
|
||||
mainRouter.get("/punishments").blockingHandler(new GETPunishments());
|
||||
mainRouter.get("/user/:id/punishments").blockingHandler(new GETUserPunishments());
|
||||
mainRouter.post("/user/:id/punish").blockingHandler(new POSTUserPunish());
|
||||
mainRouter.delete("/punishment/:id").blockingHandler(new DELETEPunishment());
|
||||
mainRouter.delete("/user/:id/punishment").blockingHandler(new DELETEUserPunishment());
|
||||
|
||||
// There's no way to do a JSON 404 page w/o doing this :(
|
||||
get("/*", new NotFound(), gson::toJson);
|
||||
post("/*", new NotFound(), gson::toJson);
|
||||
put("/*", new NotFound(), gson::toJson);
|
||||
delete("/*", new NotFound(), gson::toJson);
|
||||
mainRouter.get("/rank/:id").handler(new GETRank());
|
||||
mainRouter.get("/ranks").handler(new GETRanks());
|
||||
mainRouter.post("/rank").blockingHandler(new POSTRank());
|
||||
//mainRouter.put("/rank/:id").blockingHandler(new PUTRank());
|
||||
mainRouter.delete("/rank/:id").blockingHandler(new DELETERank());
|
||||
|
||||
mainRouter.get("/serverGroup/:id").handler(new GETServerGroup());
|
||||
mainRouter.get("/serverGroups").handler(new GETServerGroups());
|
||||
mainRouter.post("/serverGroup").blockingHandler(new POSTServerGroup());
|
||||
//mainRouter.put("/serverGroup/:id").blockingHandler(new PUTServerGroup());
|
||||
mainRouter.delete("/serverGroup/:id").blockingHandler(new DELETEServerGroup());
|
||||
|
||||
mainRouter.get("/server/:id").handler(new GETServer());
|
||||
mainRouter.get("/servers").handler(new GETServers());
|
||||
mainRouter.post("/server/heartbeat").handler(new POSTServerHeartbeat());
|
||||
mainRouter.post("/server").blockingHandler(new POSTServer());
|
||||
//mainRouter.put("/server/:id").blockingHandler(new PUTServer());
|
||||
mainRouter.delete("/server/:id").blockingHandler(new DELETEServer());
|
||||
|
||||
mainRouter.get("/staff").blockingHandler(new GETStaff());
|
||||
mainRouter.get("/user/:id").blockingHandler(new GETUser());
|
||||
mainRouter.get("/user/:id/details").blockingHandler(new GETUserDetails());
|
||||
mainRouter.get("/user/:id/meta/:serverGroup").blockingHandler(new GETUserMeta());
|
||||
mainRouter.get("/user/:id/requiresTOTP").blockingHandler(new GETUserRequiresTOTP());
|
||||
mainRouter.get("/user/:id/verifyPassword").blockingHandler(new GETUserVerifyPassword());
|
||||
mainRouter.post("/user/confirmRegister/:emailToken").blockingHandler(new POSTUserConfirmRegister());
|
||||
mainRouter.post("/user/:id/leave").handler(new POSTUserLeave());
|
||||
mainRouter.post("/user/:id/login").handler(new POSTUserLogin());
|
||||
mainRouter.post("/user/:id/notify").blockingHandler(new POSTUserNotify());
|
||||
mainRouter.post("/user/:id/register").blockingHandler(new POSTUserRegister());
|
||||
mainRouter.post("/user/:id/setupTOTP").blockingHandler(new POSTUserSetupTOTP());
|
||||
mainRouter.post("/user/:id/verifyTOTP").blockingHandler(new POSTUserVerifyTOTP());
|
||||
mainRouter.put("/user/:id/meta/:serverGroup").blockingHandler(new PUTUserMeta());
|
||||
mainRouter.delete("/user/:id/meta/:serverGroup").blockingHandler(new DELETEUserMeta());
|
||||
|
||||
mainRouter.get("/dump/:type").handler(new GETDump());
|
||||
mainRouter.get("/whoami").handler(new GETWhoAmI());
|
||||
mainRouter.post("/metrics").blockingHandler(new POSTMetrics());
|
||||
|
||||
int port = Integer.parseInt(config.getProperty("http.port"));
|
||||
webServer.requestHandler(mainRouter::accept).listen(port);
|
||||
}
|
||||
|
||||
private void convertData(String oldIp, boolean forReal) {
|
||||
// A lot of unneeded .toString()'s and cloning objects is our ghetto null validation.
|
||||
MongoDatabase importFrom = new MongoClient(oldIp).getDatabase("minehq");
|
||||
Map<ObjectId, UUID> mongoIdToUUID = new HashMap<>();
|
||||
AtomicInteger skippedUsers = new AtomicInteger();
|
||||
AtomicInteger skippedPunishments = new AtomicInteger();
|
||||
AtomicInteger skippedGrants = new AtomicInteger();
|
||||
AtomicInteger skippedIpLogs = new AtomicInteger();
|
||||
|
||||
importFrom.getCollection("user").find().forEach(new Block<Document>() {
|
||||
|
||||
@Override
|
||||
public void apply(Document user) {
|
||||
String uuidString = String.valueOf(user.get("uuid"));
|
||||
|
||||
if (uuidString == null || uuidString.length() != 32) {
|
||||
return;
|
||||
private void setupHttpClient() {
|
||||
httpClient = vertx.createHttpClient();
|
||||
}
|
||||
|
||||
UUID uuid = UUID.fromString(uuidString.replaceFirst( "([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5" ));
|
||||
|
||||
if (!UUIDUtils.isAcceptableUUID(uuid)) {
|
||||
skippedUsers.incrementAndGet();
|
||||
return;
|
||||
public static void respondJson(RoutingContext ctx, Object response) {
|
||||
respondJson(ctx, 200, response);
|
||||
}
|
||||
|
||||
mongoIdToUUID.put(user.getObjectId("_id"), uuid);
|
||||
|
||||
User created = new User(
|
||||
uuid,
|
||||
String.valueOf(user.get("name")).toString(),
|
||||
ImmutableMap.of(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
user.getString("email"),
|
||||
user.getString("phone"),
|
||||
"INVALID",
|
||||
user.getDate("joined"),
|
||||
user.getDate("joined"),
|
||||
false
|
||||
);
|
||||
|
||||
if (forReal) {
|
||||
APIv3.getDatastore().save(created);
|
||||
}
|
||||
|
||||
log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")");
|
||||
}
|
||||
});
|
||||
|
||||
importFrom.getCollection("punishment").find().forEach(new Block<Document>() {
|
||||
|
||||
@Override
|
||||
public void apply(Document punishment) {
|
||||
UUID target = mongoIdToUUID.get(((DBRef) punishment.get("user")).getId());
|
||||
|
||||
if (target == null) {
|
||||
skippedPunishments.incrementAndGet();
|
||||
return;
|
||||
}
|
||||
|
||||
// Old punishments have this value set to false to indicate they're not active anymore.
|
||||
if (punishment.containsKey("active") && !punishment.getBoolean("active")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Punishment created = new Punishment(
|
||||
new ObjectId().toString(),
|
||||
target,
|
||||
punishment.getString("reason").toString(),
|
||||
Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()),
|
||||
punishment.getDate("expires"),
|
||||
punishment.containsKey("meta") ? (punishment.get("meta") instanceof List ? ImmutableMap.of() : (Document) punishment.get("meta")) : ImmutableMap.of(),
|
||||
punishment.containsKey("addedBy") ? mongoIdToUUID.get(((DBRef) punishment.get("addedBy")).getId()) : null,
|
||||
(Date) punishment.getDate("created").clone(),
|
||||
punishment.containsKey("createdOn") ? String.valueOf(((DBRef) punishment.get("createdOn")).getId()) : "Website",
|
||||
punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE,
|
||||
punishment.containsKey("removedBy") ? (((DBRef) punishment.get("removedBy")).getCollectionName().equals("user") ? mongoIdToUUID.get(((DBRef) punishment.get("removedBy")).getId()) : null) : null,
|
||||
punishment.getDate("created"),
|
||||
punishment.containsKey("removalReason") ? punishment.getString("removalReason").toString() : ""
|
||||
);
|
||||
|
||||
if (forReal) {
|
||||
APIv3.getDatastore().save(created);
|
||||
}
|
||||
|
||||
log.info("Created punishment " + created.getId() + " (" + created.getType() + ")");
|
||||
}
|
||||
});
|
||||
|
||||
importFrom.getCollection("grant").find().forEach(new Block<Document>() {
|
||||
|
||||
@Override
|
||||
public void apply(Document grant) {
|
||||
UUID target = mongoIdToUUID.get(((DBRef) grant.get("target")).getId());
|
||||
|
||||
if (target == null) {
|
||||
skippedGrants.incrementAndGet();
|
||||
return;
|
||||
}
|
||||
|
||||
String rank = grant.getString("role");
|
||||
|
||||
if (rank.equalsIgnoreCase("unban") || rank.equalsIgnoreCase("pass") || rank.equalsIgnoreCase("pink") || rank.equalsIgnoreCase("jrdev")) {
|
||||
return;
|
||||
} else if (rank.equalsIgnoreCase("high_roller")) {
|
||||
rank = "high-roller";
|
||||
} else if (rank.equalsIgnoreCase("dev")) {
|
||||
rank = "developer";
|
||||
}
|
||||
|
||||
Grant created = new Grant(
|
||||
new ObjectId().toString(),
|
||||
target,
|
||||
grant.containsKey("comment") ? grant.getString("comment") : "",
|
||||
grant.containsKey("scope") ? ImmutableSet.copyOf((Collection<String>) grant.get("scope")) : ImmutableSet.of(),
|
||||
rank,
|
||||
grant.getDate("expires"),
|
||||
grant.containsKey("addedBy") ? mongoIdToUUID.get(((DBRef) grant.get("addedBy")).getId()) : null,
|
||||
grant.containsKey("created") ? grant.getDate("created") : new Date(),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
if (forReal) {
|
||||
APIv3.getDatastore().save(created);
|
||||
}
|
||||
|
||||
log.info("Created grant " + created.getId() + " (" + created.getRank() + ")");
|
||||
}
|
||||
});
|
||||
|
||||
importFrom.getCollection("iplog").find().forEach(new Block<Document>() {
|
||||
|
||||
@Override
|
||||
public void apply(Document ipLogEntry) {
|
||||
UUID user = mongoIdToUUID.get(((DBRef) ipLogEntry.get("user")).getId());
|
||||
|
||||
if (user == null || ipLogEntry.getString("ip") == null) {
|
||||
skippedIpLogs.incrementAndGet();
|
||||
return;
|
||||
}
|
||||
|
||||
String ip = ipLogEntry.getString("ip").replace("/", "");
|
||||
|
||||
if (!IPUtils.isValidIP(ip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Date lastSeen = ipLogEntry.getDate("lastSeen");
|
||||
|
||||
if (lastSeen == null) {
|
||||
lastSeen = new Date();
|
||||
}
|
||||
|
||||
IPLogEntry created = new IPLogEntry(
|
||||
new ObjectId().toString(),
|
||||
user,
|
||||
ip,
|
||||
lastSeen,
|
||||
lastSeen,
|
||||
((Number) ipLogEntry.get("uses")).intValue()
|
||||
|
||||
);
|
||||
|
||||
if (forReal) {
|
||||
APIv3.getDatastore().save(created);
|
||||
}
|
||||
|
||||
log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")");
|
||||
}
|
||||
});
|
||||
|
||||
log.info("Skipped " + skippedUsers.get() + " users, " + skippedPunishments.get() + " punishments, " + skippedGrants.get() + " grants, and " + skippedIpLogs.get() + " ip logs");
|
||||
public static void respondJson(RoutingContext ctx, int code, Object response) {
|
||||
ctx.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString());
|
||||
ctx.response().setStatusCode(code);
|
||||
ctx.response().end(gson.toJson(response));
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package net.frozenorb.apiv3;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
final class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new APIv3();
|
||||
System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory");
|
||||
Vertx.vertx().deployVerticle(new APIv3());
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.frozenorb.apiv3.actors;
|
||||
package net.frozenorb.apiv3.actor;
|
||||
|
||||
public interface Actor {
|
||||
|
7
src/main/java/net/frozenorb/apiv3/actor/ActorType.java
Normal file
7
src/main/java/net/frozenorb/apiv3/actor/ActorType.java
Normal file
@ -0,0 +1,7 @@
|
||||
package net.frozenorb.apiv3.actor;
|
||||
|
||||
public enum ActorType {
|
||||
|
||||
WEBSITE, BUNGEE, SERVER, USER, UNKNOWN
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.actor.actors;
|
||||
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
|
||||
public final class BungeeActor implements Actor {
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Bungee";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorType getType() {
|
||||
return ActorType.BUNGEE;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package net.frozenorb.apiv3.actors;
|
||||
package net.frozenorb.apiv3.actor.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.models.Server;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.model.Server;
|
||||
|
||||
public final class ServerActor implements Actor {
|
||||
|
||||
@ -11,14 +13,17 @@ public final class ServerActor implements Actor {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return server.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorType getType() {
|
||||
return ActorType.SERVER;
|
||||
}
|
@ -1,15 +1,21 @@
|
||||
package net.frozenorb.apiv3.actors;
|
||||
package net.frozenorb.apiv3.actor.actors;
|
||||
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
|
||||
public final class UnknownActor implements Actor {
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorType getType() {
|
||||
return ActorType.UNKNOWN;
|
||||
}
|
@ -1,38 +1,41 @@
|
||||
package net.frozenorb.apiv3.actors;
|
||||
package net.frozenorb.apiv3.actor.actors;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.models.User;
|
||||
|
||||
import java.util.Set;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.Permissions;
|
||||
|
||||
public final class UserActor implements Actor {
|
||||
|
||||
private static final Set<String> permittedUserRanks = ImmutableSet.copyOf(APIv3.getConfig().getProperty("auth.permittedUserRanks").toLowerCase().split(","));
|
||||
|
||||
@Getter private final User user;
|
||||
// We use Boolean here so we can have null = not calculated;
|
||||
// Currently having this cached isn't important as we only check
|
||||
// this once, but later on when we have non-logged in routes
|
||||
// this will be important.
|
||||
private Boolean cachedAuthorized = null;
|
||||
|
||||
public UserActor(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized() {
|
||||
if (cachedAuthorized != null) {
|
||||
return cachedAuthorized;
|
||||
} else {
|
||||
String highestRankId = user.getHighestRank().getId();
|
||||
cachedAuthorized = permittedUserRanks.contains(highestRankId.toLowerCase());
|
||||
return cachedAuthorized;
|
||||
boolean authorized = user.hasPermissionAnywhere(Permissions.SIGN_API_REQUEST);
|
||||
cachedAuthorized = authorized;
|
||||
return authorized;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return user.getLastUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorType getType() {
|
||||
return ActorType.USER;
|
||||
}
|
@ -1,15 +1,21 @@
|
||||
package net.frozenorb.apiv3.actors;
|
||||
package net.frozenorb.apiv3.actor.actors;
|
||||
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
|
||||
public final class WebsiteActor implements Actor {
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Website";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorType getType() {
|
||||
return ActorType.WEBSITE;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package net.frozenorb.apiv3.actors;
|
||||
|
||||
public enum ActorType {
|
||||
|
||||
WEBSITE, BUNGEECORD, SERVER, USER, UNKNOWN
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package net.frozenorb.apiv3.actors;
|
||||
|
||||
public final class BungeeCordActor implements Actor {
|
||||
|
||||
public boolean isAuthorized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "BungeeCord";
|
||||
}
|
||||
|
||||
public ActorType getType() {
|
||||
return ActorType.BUNGEECORD;
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +1,30 @@
|
||||
package net.frozenorb.apiv3.auditLog;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.models.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.models.User;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@UtilityClass
|
||||
public class AuditLog {
|
||||
|
||||
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType) {
|
||||
log(performedBy, performedByIp, actor, actionType, ImmutableMap.of());
|
||||
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, SingleResultCallback<AuditLogEntry> callback) {
|
||||
log(performedBy, performedByIp, actor, actionType, ImmutableMap.of(), callback);
|
||||
}
|
||||
|
||||
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, Map<String, Object> actionData) {
|
||||
APIv3.getStatsD().incrementCounter("apiv3.auditLog.insertions");
|
||||
APIv3.getDatastore().save(new AuditLogEntry(performedBy, performedByIp, actor, actionType, actionData));
|
||||
public static void log(User performedBy, String performedByIp, Actor actor, AuditLogActionType actionType, Map<String, Object> actionData, SingleResultCallback<AuditLogEntry> callback) {
|
||||
AuditLogEntry entry = new AuditLogEntry(performedBy, performedByIp, actor, actionType, actionData);
|
||||
entry.insert((ignored, error) -> {
|
||||
if (error != null) {
|
||||
callback.onResult(null, error);
|
||||
} else {
|
||||
callback.onResult(entry, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +1,29 @@
|
||||
package net.frozenorb.apiv3.auditLog;
|
||||
|
||||
import net.frozenorb.apiv3.models.AuditLogEntry;
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
|
||||
public enum AuditLogActionType {
|
||||
|
||||
DELETE_PUNISHMENT {
|
||||
|
||||
@Override
|
||||
public void revert(AuditLogEntry entry) {
|
||||
|
||||
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
|
||||
callback.onResult(false, null);
|
||||
}
|
||||
|
||||
},
|
||||
DELETE_GRANT {
|
||||
|
||||
@Override
|
||||
public void revert(AuditLogEntry entry) {
|
||||
|
||||
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
|
||||
callback.onResult(false, null);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public void revert(AuditLogEntry entry) {}
|
||||
public void revert(AuditLogEntry entry, SingleResultCallback<Boolean> callback) {
|
||||
callback.onResult(null, new UnsupportedOperationException());
|
||||
}
|
||||
|
||||
}
|
73
src/main/java/net/frozenorb/apiv3/dataImport/V2Importer.java
Normal file
73
src/main/java/net/frozenorb/apiv3/dataImport/V2Importer.java
Normal file
@ -0,0 +1,73 @@
|
||||
package net.frozenorb.apiv3.dataImport;
|
||||
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoClients;
|
||||
import com.mongodb.async.client.MongoDatabase;
|
||||
import io.vertx.core.CompositeFuture;
|
||||
import io.vertx.core.Future;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.dataImport.converters.GrantConverter;
|
||||
import net.frozenorb.apiv3.dataImport.converters.IpLogConverter;
|
||||
import net.frozenorb.apiv3.dataImport.converters.PunishmentConverter;
|
||||
import net.frozenorb.apiv3.dataImport.converters.UserConverter;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public final class V2Importer {
|
||||
|
||||
private final MongoDatabase importFrom;
|
||||
|
||||
public V2Importer(String mongoIp, String database) {
|
||||
importFrom = MongoClients.create(mongoIp).getDatabase(database);
|
||||
}
|
||||
|
||||
public void startImport(SingleResultCallback<Void> callback) {
|
||||
Map<ObjectId, UUID> oidToUniqueId = new HashMap<>();
|
||||
|
||||
importFrom.getCollection("user").find().forEach(new UserConverter(oidToUniqueId), (ignored, error) -> {
|
||||
if (error != null) {
|
||||
callback.onResult(null, error);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<Void> punishmentsFuture = Future.future();
|
||||
Future<Void> grantsFuture = Future.future();
|
||||
Future<Void> ipLogFuture = Future.future();
|
||||
|
||||
importFrom.getCollection("punishment").find().forEach(new PunishmentConverter(oidToUniqueId), new FutureCompatibilityCallback<>(punishmentsFuture));
|
||||
importFrom.getCollection("grant").find().forEach(new GrantConverter(oidToUniqueId), new FutureCompatibilityCallback<>(grantsFuture));
|
||||
importFrom.getCollection("iplog").find().forEach(new IpLogConverter(oidToUniqueId), new FutureCompatibilityCallback<>(ipLogFuture));
|
||||
|
||||
CompositeFuture.all(punishmentsFuture, grantsFuture, ipLogFuture).setHandler((result) -> {
|
||||
if (result.succeeded()) {
|
||||
callback.onResult(null, null);
|
||||
} else {
|
||||
callback.onResult(null, result.cause());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static class FutureCompatibilityCallback<T> implements SingleResultCallback<T> {
|
||||
|
||||
private final Future<T> future;
|
||||
|
||||
public FutureCompatibilityCallback(Future<T> future) {
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
public void onResult(T val, Throwable error) {
|
||||
if (error != null) {
|
||||
future.fail(error);
|
||||
} else {
|
||||
future.complete(val);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package net.frozenorb.apiv3.dataImport.converters;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.mongodb.Block;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public final class GrantConverter implements Block<Document> {
|
||||
|
||||
private final Map<ObjectId, UUID> oidToUniqueId;
|
||||
|
||||
public GrantConverter(Map<ObjectId, UUID> oidToUniqueId) {
|
||||
this.oidToUniqueId = oidToUniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Document grant) {
|
||||
UUID target = oidToUniqueId.get(((Map<String, Object>) grant.get("target")).get("$id"));
|
||||
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String rank = grant.getString("role");
|
||||
|
||||
if (rank.equalsIgnoreCase("unban") || rank.equalsIgnoreCase("pass") || rank.equalsIgnoreCase("pink") || rank.equalsIgnoreCase("jrdev")) {
|
||||
return;
|
||||
} else if (rank.equalsIgnoreCase("high_roller")) {
|
||||
rank = "high-roller";
|
||||
} else if (rank.equalsIgnoreCase("dev")) {
|
||||
rank = "developer";
|
||||
}
|
||||
|
||||
Grant created = new Grant(
|
||||
new ObjectId().toString(),
|
||||
target,
|
||||
grant.containsKey("comment") ? grant.getString("comment") : "",
|
||||
grant.containsKey("scope") ? ImmutableSet.copyOf((Collection<String>) grant.get("scope")) : ImmutableSet.of(),
|
||||
rank,
|
||||
grant.getDate("expires"),
|
||||
grant.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) grant.get("addedBy")).get("$id")) : null,
|
||||
grant.containsKey("created") ? grant.getDate("created") : new Date(),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
created.insert();
|
||||
log.info("Created grant " + created.getId() + " (" + created.getRank() + ")");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package net.frozenorb.apiv3.dataImport.converters;
|
||||
|
||||
import com.mongodb.Block;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.model.IpLogEntry;
|
||||
import net.frozenorb.apiv3.util.IpUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public final class IpLogConverter implements Block<Document> {
|
||||
|
||||
private final Map<ObjectId, UUID> oidToUniqueId;
|
||||
|
||||
public IpLogConverter(Map<ObjectId, UUID> oidToUniqueId) {
|
||||
this.oidToUniqueId = oidToUniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Document ipLogEntry) {
|
||||
UUID user = oidToUniqueId.get(((Map<String, Object>) ipLogEntry.get("user")).get("$id"));
|
||||
|
||||
if (user == null || ipLogEntry.getString("ip") == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String ip = ipLogEntry.getString("ip").replace("/", "");
|
||||
|
||||
if (!IpUtils.isValidIp(ip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Date lastSeen = ipLogEntry.getDate("lastSeen");
|
||||
|
||||
if (lastSeen == null) {
|
||||
lastSeen = new Date();
|
||||
}
|
||||
|
||||
IpLogEntry created = new IpLogEntry(
|
||||
new ObjectId().toString(),
|
||||
user,
|
||||
ip,
|
||||
lastSeen,
|
||||
lastSeen,
|
||||
((Number) ipLogEntry.get("uses")).intValue()
|
||||
|
||||
);
|
||||
|
||||
created.insert();
|
||||
log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package net.frozenorb.apiv3.dataImport.converters;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mongodb.Block;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public final class PunishmentConverter implements Block<Document> {
|
||||
|
||||
private final Map<ObjectId, UUID> oidToUniqueId;
|
||||
|
||||
public PunishmentConverter(Map<ObjectId, UUID> oidToUniqueId) {
|
||||
this.oidToUniqueId = oidToUniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Document punishment) {
|
||||
UUID target = oidToUniqueId.get(((Map<String, Object>) punishment.get("user")).get("$id"));
|
||||
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Old punishments have this value set to false to indicate they're not active anymore.
|
||||
if (punishment.containsKey("active") && !punishment.getBoolean("active")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Punishment created = new Punishment(
|
||||
new ObjectId().toString(),
|
||||
target,
|
||||
punishment.getString("reason").toString(),
|
||||
Punishment.PunishmentType.valueOf(punishment.getString("type").toUpperCase()),
|
||||
punishment.getDate("expires"),
|
||||
punishment.containsKey("meta") ? (punishment.get("meta") instanceof List ? ImmutableMap.of() : (Document) punishment.get("meta")) : ImmutableMap.of(),
|
||||
null,
|
||||
punishment.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("addedBy")).get("$id")) : null,
|
||||
(Date) punishment.getDate("created").clone(),
|
||||
punishment.containsKey("createdOn") ? String.valueOf(((Map<String, Object>) punishment.get("createdOn")).get("$id")) : "Website",
|
||||
punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE,
|
||||
punishment.containsKey("removedBy") ? (((Map<String, Object>) punishment.get("removedBy")).get("$ref").equals("user") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("removedBy")).get("$id")) : null) : null,
|
||||
punishment.containsKey("removedBy") ? (punishment.containsKey("removedAt") ? punishment.getDate("removedAt") : punishment.getDate("created")) : null,
|
||||
punishment.containsKey("removedBy") ? punishment.getString("removalReason").toString() : null
|
||||
);
|
||||
|
||||
created.insert();
|
||||
log.info("Created punishment " + created.getId() + " (" + created.getType() + ")");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package net.frozenorb.apiv3.dataImport.converters;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mongodb.Block;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.UUIDUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public final class UserConverter implements Block<Document> {
|
||||
|
||||
private final Map<ObjectId, UUID> oidToUniqueId;
|
||||
|
||||
public UserConverter(Map<ObjectId, UUID> oidToUniqueId) {
|
||||
this.oidToUniqueId = oidToUniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Document user) {
|
||||
String uuidString = String.valueOf(user.get("uuid"));
|
||||
|
||||
if (uuidString == null || uuidString.length() != 32 || user.get("name") == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID uuid = UUID.fromString(uuidString.replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5"));
|
||||
|
||||
if (!UUIDUtils.isAcceptableUUID(uuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
oidToUniqueId.put(user.getObjectId("_id"), uuid);
|
||||
|
||||
User created = new User(
|
||||
uuid,
|
||||
user.get("name").toString(),
|
||||
ImmutableMap.of(user.get("name").toString(), user.getDate("joined")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
user.getString("email"),
|
||||
user.getString("phone"),
|
||||
"INVALID",
|
||||
user.getDate("joined"),
|
||||
user.getDate("joined"),
|
||||
false
|
||||
);
|
||||
|
||||
created.insert();
|
||||
log.info("Created user " + created.getLastUsername() + " (" + created.getId() + ")");
|
||||
}
|
||||
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package net.frozenorb.apiv3.filters;
|
||||
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actors.*;
|
||||
import net.frozenorb.apiv3.models.Server;
|
||||
import net.frozenorb.apiv3.models.User;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Filter;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Spark;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public final class ActorAttributeFilter implements Filter {
|
||||
|
||||
public void handle(Request req, Response res) {
|
||||
String authHeader = req.headers("Authorization");
|
||||
String mhqAuthHeader = req.headers("MHQ-Authorization");
|
||||
|
||||
if (authHeader != null) {
|
||||
req.attribute("actor", processBasicAuthorization(authHeader, res));
|
||||
} else if (mhqAuthHeader != null) {
|
||||
req.attribute("actor", processMHQAuthorization(mhqAuthHeader));
|
||||
} else {
|
||||
req.attribute("actor", new UnknownActor());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // We purposely get the User by their last username.
|
||||
private Actor processBasicAuthorization(String authHeader, Response res) {
|
||||
String encodedHeader = authHeader.substring("Basic ".length());
|
||||
String[] credentials = new String(Base64.getDecoder().decode(encodedHeader.getBytes())).split(":");
|
||||
|
||||
if (credentials.length == 2) {
|
||||
User user = User.byLastUsername(credentials[0]);
|
||||
String password = credentials[1];
|
||||
|
||||
if (user != null && user.getPassword() != null && user.checkPassword(password)) {
|
||||
return new UserActor(user);
|
||||
}
|
||||
}
|
||||
|
||||
res.header("WWW-Authenticate", "Basic realm=\"MineHQ\"");
|
||||
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Failed to authorize as " + credentials[0] + ".")));
|
||||
return null;
|
||||
}
|
||||
|
||||
private Actor processMHQAuthorization(String authHeader) {
|
||||
String[] split = authHeader.split(" ");
|
||||
|
||||
if (split.length >= 2) {
|
||||
String type = split[0];
|
||||
|
||||
if (type.equals("Website") && split.length == 2) {
|
||||
String givenKey = split[1];
|
||||
String properKey = APIv3.getConfig().getProperty("auth.websiteApiKey");
|
||||
|
||||
if (givenKey.equals(properKey)) {
|
||||
return new WebsiteActor();
|
||||
}
|
||||
} else if (type.equals("Server") && split.length == 3) {
|
||||
Server server = Server.byId(split[1]);
|
||||
|
||||
if (server == null) {
|
||||
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.notFound("Server", split[1])));
|
||||
}
|
||||
|
||||
String givenKey = split[2];
|
||||
String properKey = server.getApiKey();
|
||||
|
||||
if (givenKey.equals(properKey)) {
|
||||
return new ServerActor(server);
|
||||
}
|
||||
} else if (type.equals("BungeeCord") && split.length == 2) {
|
||||
String givenKey = split[1];
|
||||
String properKey = APIv3.getConfig().getProperty("auth.bungeeCordApiKey");
|
||||
|
||||
if (givenKey.equals(properKey)) {
|
||||
return new BungeeCordActor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Failed to authorize.")));
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package net.frozenorb.apiv3.filters;
|
||||
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Filter;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Spark;
|
||||
|
||||
public final class AuthorizationFilter implements Filter {
|
||||
|
||||
public void handle(Request req, Response res) {
|
||||
Actor actor = req.attribute("actor");
|
||||
|
||||
if (!actor.isAuthorized()) {
|
||||
APIv3.getStatsD().incrementCounter("apiv3.http.unauthorized");
|
||||
res.header("WWW-Authenticate", "Basic realm=\"MineHQ\"");
|
||||
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Unauthorized access: Please authorize as an approved actor. You're currently authorized as " + actor.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package net.frozenorb.apiv3.filters;
|
||||
|
||||
import spark.Filter;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
public final class ContentTypeFilter implements Filter {
|
||||
|
||||
public void handle(Request req, Response res) {
|
||||
res.type("application/json");
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package net.frozenorb.apiv3.filters;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import spark.Filter;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@Slf4j
|
||||
public final class LoggingFilter implements Filter {
|
||||
|
||||
@Getter @Setter private static boolean debug = false;
|
||||
|
||||
public void handle(Request req, Response res) {
|
||||
Actor actor = req.attribute("actor");
|
||||
log.info("(" + actor.getName() + " - " + actor.getType() + ") " + req.requestMethod().toUpperCase() + " " + req.pathInfo() + (debug ? "\n" + res.body() : ""));
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package net.frozenorb.apiv3.filters;
|
||||
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import spark.Filter;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
public final class MetricsAfterFilter implements Filter {
|
||||
|
||||
public void handle(Request req, Response res) {
|
||||
long started = req.attribute("requestStarted");
|
||||
APIv3.getStatsD().recordExecutionTime("apiv3.http.executionTime", System.currentTimeMillis() - started);
|
||||
APIv3.getStatsD().incrementCounter("apiv3.http.requests");
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package net.frozenorb.apiv3.filters;
|
||||
|
||||
import spark.Filter;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
public final class MetricsBeforeFilter implements Filter {
|
||||
|
||||
public void handle(Request req, Response res) {
|
||||
req.attribute("requestStarted", System.currentTimeMillis());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package net.frozenorb.apiv3.handler;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actor.actors.*;
|
||||
import net.frozenorb.apiv3.model.Server;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Base64;
|
||||
|
||||
public final class ActorAttributeHandler implements Handler<RoutingContext> {
|
||||
|
||||
@Override
|
||||
public void handle(RoutingContext ctx) {
|
||||
String authorizationHeader = ctx.request().getHeader("Authorization");
|
||||
String mhqAuthorizationHeader = ctx.request().getHeader("MHQ-Authorization");
|
||||
|
||||
if (authorizationHeader != null) {
|
||||
processBasicAuthorization(authorizationHeader, ctx);
|
||||
} else if (mhqAuthorizationHeader != null) {
|
||||
processMHQAuthorization(mhqAuthorizationHeader, ctx);
|
||||
} else {
|
||||
processNoAuthorization(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBasicAuthorization(String authHeader, RoutingContext ctx) {
|
||||
String encodedHeader = authHeader.substring("Basic ".length());
|
||||
String decodedHeader = new String(Base64.getDecoder().decode(encodedHeader.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
|
||||
String[] splitHeader = decodedHeader.split(":");
|
||||
|
||||
if (splitHeader.length != 2) {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize.");
|
||||
return;
|
||||
}
|
||||
|
||||
String username = splitHeader[0];
|
||||
String password = splitHeader[1];
|
||||
|
||||
User.findByLastUsername(username, (user, error) -> {
|
||||
if (error != null) {
|
||||
ErrorUtils.respondInternalError(ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user != null && user.checkPassword(password)) {
|
||||
ctx.put("actor", new UserActor(user));
|
||||
ctx.next();
|
||||
} else {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + username + ".");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void processMHQAuthorization(String authHeader, RoutingContext ctx) {
|
||||
String[] splitHeader = authHeader.split(" ");
|
||||
|
||||
if (splitHeader.length < 2) {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize.");
|
||||
return;
|
||||
}
|
||||
|
||||
String type = splitHeader[0];
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case "website":
|
||||
String givenWebsiteKey = splitHeader[1];
|
||||
String properWebsiteKey = APIv3.getConfig().getProperty("auth.websiteApiKey");
|
||||
|
||||
if (givenWebsiteKey.equals(properWebsiteKey)) {
|
||||
ctx.put("actor", new WebsiteActor());
|
||||
ctx.next();
|
||||
} else {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as website.");
|
||||
}
|
||||
|
||||
break;
|
||||
case "server":
|
||||
Server server = Server.findById(splitHeader[1]);
|
||||
|
||||
if (server == null) {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize: Server " + splitHeader[1] + " not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (splitHeader.length != 3) {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize: Key not provided");
|
||||
return;
|
||||
}
|
||||
|
||||
String givenServerKey = splitHeader[2];
|
||||
|
||||
if (givenServerKey.equals(server.getApiKey())) {
|
||||
ctx.put("actor", new ServerActor(server));
|
||||
ctx.next();
|
||||
} else {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + server.getId() + ".");
|
||||
}
|
||||
|
||||
break;
|
||||
case "bungee":
|
||||
String givenBungeeKey = splitHeader[1];
|
||||
String properBungeeKey = APIv3.getConfig().getProperty("auth.bungeeApiKey");
|
||||
|
||||
if (givenBungeeKey.equals(properBungeeKey)) {
|
||||
ctx.put("actor", new BungeeActor());
|
||||
ctx.next();
|
||||
} else {
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as bungee.");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + type + ".");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void processNoAuthorization(RoutingContext ctx) {
|
||||
ctx.put("actor", new UnknownActor());
|
||||
ctx.next();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package net.frozenorb.apiv3.handler;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.actor.actors.ServerActor;
|
||||
import net.frozenorb.apiv3.model.Server;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class AuthorizationHandler implements Handler<RoutingContext> {
|
||||
|
||||
@Override
|
||||
public void handle(RoutingContext ctx) {
|
||||
Actor actor = ctx.get("actor");
|
||||
|
||||
if (!actor.isAuthorized()) {
|
||||
ErrorUtils.respondGeneric(ctx, 403, "Please authorize as an approved actor. You're currently authorized as " + actor.getName() + " (" + actor.getType() + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if (actor.getType() == ActorType.SERVER) {
|
||||
Server server = ((ServerActor) actor).getServer();
|
||||
String[] serverAddress = server.getServerIp().split(":");
|
||||
String expectedHost = serverAddress[0];
|
||||
String remoteHost = ctx.request().remoteAddress().host();
|
||||
|
||||
if (!expectedHost.equals(remoteHost)) {
|
||||
ErrorUtils.respondGeneric(ctx, 403, "Failed to authorize: Cannot authorize as " + server.getId() + " from given ip.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.next();
|
||||
}
|
||||
|
||||
}
|
90
src/main/java/net/frozenorb/apiv3/model/AuditLogEntry.java
Normal file
90
src/main/java/net/frozenorb/apiv3/model/AuditLogEntry.java
Normal file
@ -0,0 +1,90 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Entity
|
||||
public final class AuditLogEntry {
|
||||
|
||||
private static final MongoCollection<AuditLogEntry> auditLogCollection = APIv3.getDatabase().getCollection("auditLog", AuditLogEntry.class);
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private UUID user;
|
||||
@Getter private String userIp;
|
||||
@Getter private Date performedAt;
|
||||
@Getter private String actorName;
|
||||
@Getter private ActorType actorType;
|
||||
@Getter private AuditLogActionType type;
|
||||
@Getter private Map<String, Object> metadata;
|
||||
|
||||
public static List<AuditLogEntry> findAllSync() {
|
||||
return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", -1)));
|
||||
}
|
||||
|
||||
public static List<AuditLogEntry> findAllPaginatedSync(int skip, int pageSize) {
|
||||
return SyncUtils.blockMulti(auditLogCollection.find().sort(new Document("performedAt", -1)).skip(skip).limit(pageSize));
|
||||
}
|
||||
|
||||
public static AuditLogEntry findByIdSync(String id) {
|
||||
return SyncUtils.blockOne(auditLogCollection.find(new Document("_id", id)));
|
||||
}
|
||||
|
||||
public static List<AuditLogEntry> findByUserSync(User user) {
|
||||
return findByUserSync(user.getId());
|
||||
}
|
||||
|
||||
public static List<AuditLogEntry> findByUserSync(UUID user) {
|
||||
return SyncUtils.blockMulti(auditLogCollection.find(new Document("user", user)));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<AuditLogEntry>> callback) {
|
||||
auditLogCollection.find().sort(new Document("performedAt", -1)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<AuditLogEntry>> callback) {
|
||||
auditLogCollection.find().sort(new Document("performedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<AuditLogEntry> callback) {
|
||||
auditLogCollection.find(new Document("_id", id)).first(callback);
|
||||
}
|
||||
|
||||
public static void findByUser(User user, SingleResultCallback<List<AuditLogEntry>> callback) {
|
||||
findByUser(user.getId(), callback);
|
||||
}
|
||||
|
||||
public static void findByUser(UUID user, SingleResultCallback<List<AuditLogEntry>> callback) {
|
||||
auditLogCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public AuditLogEntry() {} // For Morphia
|
||||
|
||||
public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map<String, Object> metadata) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.userIp = userIp;
|
||||
this.performedAt = new Date();
|
||||
this.actorName = actor.getName();
|
||||
this.actorType = actor.getType();
|
||||
this.type = type;
|
||||
this.metadata = ImmutableMap.copyOf(metadata);
|
||||
}
|
||||
|
||||
public void insert(SingleResultCallback<Void> callback) {
|
||||
auditLogCollection.insertOne(this, callback);
|
||||
}
|
||||
|
||||
}
|
163
src/main/java/net/frozenorb/apiv3/model/Grant.java
Normal file
163
src/main/java/net/frozenorb/apiv3/model/Grant.java
Normal file
@ -0,0 +1,163 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
public final class Grant {
|
||||
|
||||
private static final MongoCollection<Grant> grantsCollection = APIv3.getDatabase().getCollection("grants", Grant.class);
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private UUID user;
|
||||
@Getter private String reason;
|
||||
@Getter private Set<String> scopes = new HashSet<>(); // So on things w/o scopes we still load properly (Morphia drops empty sets)
|
||||
@Getter private String rank;
|
||||
@Getter private Date expiresAt;
|
||||
|
||||
@Getter private UUID addedBy;
|
||||
@Getter private Date addedAt;
|
||||
|
||||
@Getter private UUID removedBy;
|
||||
@Getter private Date removedAt;
|
||||
@Getter private String removalReason;
|
||||
|
||||
public static List<Grant> findAllSync() {
|
||||
return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", -1)));
|
||||
}
|
||||
|
||||
public static List<Grant> findAllPaginatedSync(int skip, int pageSize) {
|
||||
return SyncUtils.blockMulti(grantsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize));
|
||||
}
|
||||
|
||||
public static List<Grant> findByRankSync(Collection<Rank> ranks) {
|
||||
Collection<String> convertedRanks = ranks.stream().map(Rank::getId).collect(Collectors.toList());
|
||||
return SyncUtils.blockMulti(grantsCollection.find(new Document("rank", new Document("$in", convertedRanks))));
|
||||
}
|
||||
|
||||
public static Grant findByIdSync(String id) {
|
||||
return SyncUtils.blockOne(grantsCollection.find(new Document("_id", id)));
|
||||
}
|
||||
|
||||
public static List<Grant> findByUserSync(User user) {
|
||||
return findByUserSync(user.getId());
|
||||
}
|
||||
|
||||
public static List<Grant> findByUserSync(UUID user) {
|
||||
return SyncUtils.blockMulti(grantsCollection.find(new Document("user", user)));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<Grant>> callback) {
|
||||
grantsCollection.find().sort(new Document("addedAt", -1)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Grant>> callback) {
|
||||
grantsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findByRank(Collection<Rank> ranks, SingleResultCallback<List<Grant>> callback) {
|
||||
Collection<String> convertedRanks = ranks.stream().map(Rank::getId).collect(Collectors.toList());
|
||||
grantsCollection.find(new Document("rank", new Document("$in", convertedRanks))).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<Grant> callback) {
|
||||
grantsCollection.find(new Document("_id", id)).first(callback);
|
||||
}
|
||||
|
||||
public static void findByUser(User user, SingleResultCallback<List<Grant>> callback) {
|
||||
findByUser(user.getId(), callback);
|
||||
}
|
||||
|
||||
public static void findByUser(UUID user, SingleResultCallback<List<Grant>> callback) {
|
||||
grantsCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findByUserGrouped(Iterable<UUID> users, SingleResultCallback<Map<UUID, List<Grant>>> callback) {
|
||||
grantsCollection.find(new Document("user", new Document("$in", users))).into(new ArrayList<>(), (grants, error) -> {
|
||||
if (error != null) {
|
||||
callback.onResult(null, error);
|
||||
} else {
|
||||
Map<UUID, List<Grant>> result = new HashMap<>();
|
||||
|
||||
for (UUID user : users) {
|
||||
result.put(user, new ArrayList<>());
|
||||
}
|
||||
|
||||
for (Grant grant : grants) {
|
||||
result.get(grant.getUser()).add(grant);
|
||||
}
|
||||
|
||||
callback.onResult(result, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Grant() {} // For Morphia
|
||||
|
||||
public Grant(User user, String reason, Set<ServerGroup> scopes, Rank rank, Date expiresAt, User addedBy) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.reason = reason;
|
||||
this.scopes = new HashSet<>(Collections2.transform(scopes, ServerGroup::getId));
|
||||
this.rank = rank.getId();
|
||||
this.expiresAt = expiresAt;
|
||||
this.addedBy = addedBy == null ? null : addedBy.getId();
|
||||
this.addedAt = new Date();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return !(isExpired() || isRemoved());
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
if (expiresAt == null) {
|
||||
return false; // Never expires
|
||||
} else {
|
||||
return expiresAt.before(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRemoved() {
|
||||
return removedBy != null;
|
||||
}
|
||||
|
||||
public boolean appliesOn(ServerGroup serverGroup) {
|
||||
return isGlobal() || scopes.contains(serverGroup.getId());
|
||||
}
|
||||
|
||||
public boolean isGlobal() {
|
||||
return scopes.isEmpty();
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
grantsCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete(User removedBy, String reason) {
|
||||
this.removedBy = removedBy.getId();
|
||||
this.removedAt = new Date();
|
||||
this.removalReason = reason;
|
||||
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
grantsCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
161
src/main/java/net/frozenorb/apiv3/model/IpBan.java
Normal file
161
src/main/java/net/frozenorb/apiv3/model/IpBan.java
Normal file
@ -0,0 +1,161 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import net.frozenorb.apiv3.util.TimeUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
public final class IpBan {
|
||||
|
||||
private static final MongoCollection<IpBan> ipBansCollection = APIv3.getDatabase().getCollection("ipBans", IpBan.class);
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private String userIp;
|
||||
@Getter private String reason;
|
||||
@Getter private Date expiresAt;
|
||||
|
||||
@Getter private UUID addedBy;
|
||||
@Getter private Date addedAt;
|
||||
@Getter private String actorName;
|
||||
@Getter private ActorType actorType;
|
||||
|
||||
@Getter private UUID removedBy;
|
||||
@Getter private Date removedAt;
|
||||
@Getter private String removalReason;
|
||||
|
||||
public static List<IpBan> findAllSync() {
|
||||
return SyncUtils.blockMulti(ipBansCollection.find().sort(new Document("addedAt", -1)));
|
||||
}
|
||||
|
||||
public static List<IpBan> findAllPaginatedSync(int skip, int pageSize) {
|
||||
return SyncUtils.blockMulti(ipBansCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize));
|
||||
}
|
||||
|
||||
public static IpBan findByIdSync(String id) {
|
||||
return SyncUtils.blockOne(ipBansCollection.find(new Document("_id", id)));
|
||||
}
|
||||
|
||||
public static List<IpBan> findByIpSync(String userIp) {
|
||||
return SyncUtils.blockMulti(ipBansCollection.find(new Document("userIp", userIp)));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<IpBan>> callback) {
|
||||
ipBansCollection.find().sort(new Document("addedAt", -1)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<IpBan>> callback) {
|
||||
ipBansCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<IpBan> callback) {
|
||||
ipBansCollection.find(new Document("_id", id)).first(callback);
|
||||
}
|
||||
|
||||
public static void findByIp(String userIp, SingleResultCallback<List<IpBan>> callback) {
|
||||
ipBansCollection.find(new Document("userIp", userIp)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findByIpGrouped(Iterable<String> userIps, SingleResultCallback<Map<String, List<IpBan>>> callback) {
|
||||
ipBansCollection.find(new Document("userIp", new Document("$in", userIps))).into(new ArrayList<>(), (ipBans, error) -> {
|
||||
if (error != null) {
|
||||
callback.onResult(null, error);
|
||||
} else {
|
||||
Map<String, List<IpBan>> result = new HashMap<>();
|
||||
|
||||
for (String userIp : userIps) {
|
||||
result.put(userIp, new ArrayList<>());
|
||||
}
|
||||
|
||||
for (IpBan ipBan : ipBans) {
|
||||
result.get(ipBan.getUserIp()).add(ipBan);
|
||||
}
|
||||
|
||||
callback.onResult(result, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public IpBan() {} // For Morphia
|
||||
|
||||
public IpBan(String userIp, Punishment linked) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.userIp = userIp;
|
||||
this.reason = linked.getReason();
|
||||
this.expiresAt = linked.getExpiresAt();
|
||||
this.addedBy = linked.getAddedBy();
|
||||
this.addedAt = new Date();
|
||||
this.actorName = linked.getActorName();
|
||||
this.actorType = linked.getActorType();
|
||||
}
|
||||
|
||||
public IpBan(String userIp, String reason, Date expiresAt, User addedBy, Actor actor) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.userIp = userIp;
|
||||
this.reason = reason;
|
||||
this.expiresAt = expiresAt;
|
||||
this.addedBy = addedBy == null ? null : addedBy.getId();
|
||||
this.addedAt = new Date();
|
||||
this.actorName = actor.getName();
|
||||
this.actorType = actor.getType();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return !(isExpired() || isRemoved());
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
if (expiresAt == null) {
|
||||
return false; // Never expires
|
||||
} else {
|
||||
return expiresAt.before(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRemoved() {
|
||||
return removedBy != null;
|
||||
}
|
||||
|
||||
public String getAccessDenialReason() {
|
||||
String accessDenialReason = "Your ip address has been suspended from the MineHQ Network. \n\n";
|
||||
|
||||
if (getExpiresAt() != null) {
|
||||
accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), new Date()));
|
||||
} else {
|
||||
accessDenialReason += "Appeal at MineHQ.com/appeal";
|
||||
}
|
||||
|
||||
return accessDenialReason;
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
ipBansCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete(User removedBy, String reason) {
|
||||
this.removedBy = removedBy.getId();
|
||||
this.removedAt = new Date();
|
||||
this.removalReason = reason;
|
||||
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
ipBansCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
110
src/main/java/net/frozenorb/apiv3/model/IpLogEntry.java
Normal file
110
src/main/java/net/frozenorb/apiv3/model/IpLogEntry.java
Normal file
@ -0,0 +1,110 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
public final class IpLogEntry {
|
||||
|
||||
private static final MongoCollection<IpLogEntry> ipLogCollection = APIv3.getDatabase().getCollection("ipLog", IpLogEntry.class);
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private UUID user;
|
||||
@Getter private String userIp;
|
||||
@Getter private Date firstSeenAt;
|
||||
@Getter private Date lastSeenAt;
|
||||
@Getter private int uses;
|
||||
|
||||
public static List<IpLogEntry> findAllSync() {
|
||||
return SyncUtils.blockMulti(ipLogCollection.find().sort(new Document("lastSeenAt", -1)));
|
||||
}
|
||||
|
||||
public static IpLogEntry findByIdSync(String id) {
|
||||
return SyncUtils.blockOne(ipLogCollection.find(new Document("_id", id)));
|
||||
}
|
||||
|
||||
public static List<IpLogEntry> findByUserSync(User user) {
|
||||
return findByUserSync(user.getId());
|
||||
}
|
||||
|
||||
public static List<IpLogEntry> findByUserSync(UUID user) {
|
||||
return SyncUtils.blockMulti(ipLogCollection.find(new Document("user", user)).sort(new Document("lastSeenAt", -1)));
|
||||
}
|
||||
|
||||
public static IpLogEntry findByUserAndIpSync(User user, String userIp) {
|
||||
return findByUserAndIpSync(user.getId(), userIp);
|
||||
}
|
||||
|
||||
public static IpLogEntry findByUserAndIpSync(UUID user, String userIp) {
|
||||
return SyncUtils.blockOne(ipLogCollection.find(new Document("user", user).append("userIp", userIp)));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<IpLogEntry>> callback) {
|
||||
ipLogCollection.find().sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<IpLogEntry> callback) {
|
||||
ipLogCollection.find(new Document("_id", id)).first(callback);
|
||||
}
|
||||
|
||||
public static void findByUser(User user, SingleResultCallback<List<IpLogEntry>> callback) {
|
||||
findByUser(user.getId(), callback);
|
||||
}
|
||||
|
||||
public static void findByUser(UUID user, SingleResultCallback<List<IpLogEntry>> callback) {
|
||||
ipLogCollection.find(new Document("user", user)).sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findByUserAndIp(User user, String userIp, SingleResultCallback<IpLogEntry> callback) {
|
||||
findByUserAndIp(user.getId(), userIp, callback);
|
||||
}
|
||||
|
||||
public static void findByUserAndIp(UUID user, String userIp, SingleResultCallback<IpLogEntry> callback) {
|
||||
ipLogCollection.find(new Document("user", user).append("userIp", userIp)).first(callback);
|
||||
}
|
||||
|
||||
public IpLogEntry() {} // For Morphia
|
||||
|
||||
public IpLogEntry(User user, String userIp) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.userIp = userIp;
|
||||
this.firstSeenAt = new Date();
|
||||
this.lastSeenAt = new Date();
|
||||
this.uses = 0;
|
||||
}
|
||||
|
||||
public void used() {
|
||||
this.lastSeenAt = new Date();
|
||||
this.uses++;
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
ipLogCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
ipLogCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
public final class NotificationTemplate {
|
||||
|
||||
private static final MongoCollection<NotificationTemplate> notificationTemplatesCollection = APIv3.getDatabase().getCollection("notificationTemplates", NotificationTemplate.class);
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter @Setter private String subject;
|
||||
@Getter @Setter private String body;
|
||||
|
||||
public static List<NotificationTemplate> findAllSync() {
|
||||
return SyncUtils.blockMulti(notificationTemplatesCollection.find());
|
||||
}
|
||||
|
||||
public static NotificationTemplate findByIdSync(String id) {
|
||||
return SyncUtils.blockOne(notificationTemplatesCollection.find(new Document("_id", id)));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<NotificationTemplate>> callback) {
|
||||
notificationTemplatesCollection.find().into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<NotificationTemplate> callback) {
|
||||
notificationTemplatesCollection.find(new Document("_id", id)).first(callback);
|
||||
}
|
||||
|
||||
public NotificationTemplate() {} // For Morphia
|
||||
|
||||
public NotificationTemplate(String id, String subject, String body) {
|
||||
this.id = id;
|
||||
this.subject = subject;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public String fillSubject(Map<String, Object> replacements) {
|
||||
return fill(subject, replacements);
|
||||
}
|
||||
|
||||
public String fillBody(Map<String, Object> replacements) {
|
||||
return fill(body, replacements);
|
||||
}
|
||||
|
||||
private String fill(String working, Map<String, Object> replacements) {
|
||||
for (Map.Entry<String, Object> replacement : replacements.entrySet()) {
|
||||
String key = replacement.getKey();
|
||||
String value = String.valueOf(replacement.getValue());
|
||||
|
||||
working = working.replace("%" + key + "%", value);
|
||||
}
|
||||
|
||||
return working;
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
notificationTemplatesCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
notificationTemplatesCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
|
||||
notificationTemplatesCollection.deleteOne(new Document("_id", id), callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
217
src/main/java/net/frozenorb/apiv3/model/Punishment.java
Normal file
217
src/main/java/net/frozenorb/apiv3/model/Punishment.java
Normal file
@ -0,0 +1,217 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import net.frozenorb.apiv3.util.TimeUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
public final class Punishment {
|
||||
|
||||
private static final MongoCollection<Punishment> punishmentsCollection = APIv3.getDatabase().getCollection("punishments", Punishment.class);
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private UUID user;
|
||||
@Getter private String reason;
|
||||
@Getter private PunishmentType type;
|
||||
@Getter private Date expiresAt;
|
||||
@Getter private Map<String, Object> metadata;
|
||||
@Getter private String linkedIpBanId;
|
||||
|
||||
@Getter private UUID addedBy;
|
||||
@Getter private Date addedAt;
|
||||
@Getter private String actorName;
|
||||
@Getter private ActorType actorType;
|
||||
|
||||
@Getter private UUID removedBy;
|
||||
@Getter private Date removedAt;
|
||||
@Getter private String removalReason;
|
||||
|
||||
public static List<Punishment> findAllSync() {
|
||||
return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", -1)));
|
||||
}
|
||||
|
||||
public static List<Punishment> findAllPaginatedSync(int skip, int pageSize) {
|
||||
return SyncUtils.blockMulti(punishmentsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize));
|
||||
}
|
||||
|
||||
public static List<Punishment> findByTypeSync(Collection<PunishmentType> types) {
|
||||
Collection<String> convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList());
|
||||
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("type", new Document("$in", convertedTypes))));
|
||||
}
|
||||
|
||||
public static Punishment findByIdSync(String id) {
|
||||
return SyncUtils.blockOne(punishmentsCollection.find(new Document("_id", id)));
|
||||
}
|
||||
|
||||
public static List<Punishment> findByUserSync(User user) {
|
||||
return findByUserSync(user.getId());
|
||||
}
|
||||
|
||||
public static List<Punishment> findByUserSync(UUID user) {
|
||||
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user)));
|
||||
}
|
||||
|
||||
public static List<Punishment> findByUserAndTypeSync(User user, Collection<PunishmentType> types) {
|
||||
return findByUserAndTypeSync(user.getId(), types);
|
||||
}
|
||||
|
||||
public static List<Punishment> findByUserAndTypeSync(UUID user, Collection<PunishmentType> types) {
|
||||
Collection<String> convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList());
|
||||
return SyncUtils.blockMulti(punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes))));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<Punishment>> callback) {
|
||||
punishmentsCollection.find().sort(new Document("addedAt", -1)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findAllPaginated(int skip, int pageSize, SingleResultCallback<List<Punishment>> callback) {
|
||||
punishmentsCollection.find().sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findByType(Collection<PunishmentType> types, SingleResultCallback<List<Punishment>> callback) {
|
||||
Collection<String> convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList());
|
||||
punishmentsCollection.find(new Document("type", new Document("$in", convertedTypes))).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<Punishment> callback) {
|
||||
punishmentsCollection.find(new Document("_id", id)).first(callback);
|
||||
}
|
||||
|
||||
public static void findByUser(User user, SingleResultCallback<List<Punishment>> callback) {
|
||||
findByUser(user.getId(), callback);
|
||||
}
|
||||
|
||||
public static void findByUser(UUID user, SingleResultCallback<List<Punishment>> callback) {
|
||||
punishmentsCollection.find(new Document("user", user)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findByUserGrouped(Iterable<UUID> users, SingleResultCallback<Map<UUID, List<Punishment>>> callback) {
|
||||
punishmentsCollection.find(new Document("user", new Document("$in", users))).into(new ArrayList<>(), (punishments, error) -> {
|
||||
if (error != null) {
|
||||
callback.onResult(null, error);
|
||||
} else {
|
||||
Map<UUID, List<Punishment>> result = new HashMap<>();
|
||||
|
||||
for (UUID user : users) {
|
||||
result.put(user, new ArrayList<>());
|
||||
}
|
||||
|
||||
for (Punishment punishment : punishments) {
|
||||
result.get(punishment.getUser()).add(punishment);
|
||||
}
|
||||
|
||||
callback.onResult(result, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void findByUserAndType(User user, Collection<PunishmentType> types, SingleResultCallback<List<Punishment>> callback) {
|
||||
findByUserAndType(user.getId(), types, callback);
|
||||
}
|
||||
|
||||
public static void findByUserAndType(UUID user, Collection<PunishmentType> types, SingleResultCallback<List<Punishment>> callback) {
|
||||
Collection<String> convertedTypes = types.stream().map(PunishmentType::name).collect(Collectors.toList());
|
||||
punishmentsCollection.find(new Document("user", user).append("type", new Document("$in", convertedTypes))).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public Punishment() {} // For Morphia
|
||||
|
||||
public Punishment(User user, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map<String, Object> metadata) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.reason = reason;
|
||||
this.type = type;
|
||||
this.expiresAt = expiresAt;
|
||||
this.addedBy = addedBy == null ? null : addedBy.getId();
|
||||
this.addedAt = new Date();
|
||||
this.actorName = actor.getName();
|
||||
this.actorType = actor.getType();
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return !(isExpired() || isRemoved());
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
if (expiresAt == null) {
|
||||
return false; // Never expires
|
||||
} else {
|
||||
return expiresAt.before(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRemoved() {
|
||||
return removedBy != null;
|
||||
}
|
||||
|
||||
public String getAccessDenialReason() {
|
||||
switch (type) {
|
||||
case BLACKLIST:
|
||||
return "Your account has been blacklisted from the MineHQ Network. \n\nThis type of punishment cannot be appealed.";
|
||||
case BAN:
|
||||
String accessDenialReason = "Your account has been suspended from the MineHQ Network. \n\n";
|
||||
|
||||
if (getExpiresAt() != null) {
|
||||
accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), new Date()));
|
||||
} else {
|
||||
accessDenialReason += "Appeal at MineHQ.com/appeal";
|
||||
}
|
||||
|
||||
return accessDenialReason;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void linkIpBan(IpBan ipBan) {
|
||||
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
punishmentsCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete(User removedBy, String reason) {
|
||||
this.removedBy = removedBy.getId();
|
||||
this.removedAt = new Date();
|
||||
this.removalReason = reason;
|
||||
|
||||
if (linkedIpBanId != null) {
|
||||
IpBan ipBan = IpBan.findByIdSync(linkedIpBanId);
|
||||
|
||||
if (ipBan != null && ipBan.isActive()) {
|
||||
ipBan.delete(removedBy, "Linked punishment removed: " + reason);
|
||||
}
|
||||
}
|
||||
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
punishmentsCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public enum PunishmentType {
|
||||
|
||||
BLACKLIST, BAN, MUTE, WARN
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +1,29 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Indexed;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Entity(value = "ranks", noClassnameStored = true)
|
||||
@Entity
|
||||
public final class Rank {
|
||||
|
||||
private static final MongoCollection<Rank> ranksCollection = APIv3.getDatabase().getCollection("ranks", Rank.class);
|
||||
|
||||
private static Map<String, Rank> rankCache = null;
|
||||
private static List<Rank> rankAltCache = null;
|
||||
private static long rankCacheUpdated = 0;
|
||||
@ -22,18 +33,18 @@ public final class Rank {
|
||||
@Getter private String displayName;
|
||||
@Getter private String gameColor;
|
||||
@Getter private String websiteColor;
|
||||
@Getter @Indexed private boolean staffRank;
|
||||
@Getter private boolean staffRank;
|
||||
|
||||
public static Rank byId(String id) {
|
||||
updateCacheIfNeeded();
|
||||
return rankCache.get(id);
|
||||
}
|
||||
|
||||
public static List<Rank> values() {
|
||||
public static List<Rank> findAll() {
|
||||
updateCacheIfNeeded();
|
||||
return ImmutableList.copyOf(rankAltCache);
|
||||
}
|
||||
|
||||
public static Rank findById(String id) {
|
||||
updateCacheIfNeeded();
|
||||
return rankCache.get(id);
|
||||
}
|
||||
|
||||
public Rank() {} // For Morphia
|
||||
|
||||
public Rank(String id, int weight, String displayName, String gameColor, String websiteColor, boolean staffRank) {
|
||||
@ -45,16 +56,14 @@ public final class Rank {
|
||||
this.staffRank = staffRank;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
APIv3.getDatastore().delete(this);
|
||||
}
|
||||
|
||||
private static void updateCacheIfNeeded() {
|
||||
if (rankCache == null || (System.currentTimeMillis() - rankCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) {
|
||||
Map<String, Rank> working = new HashMap<>();
|
||||
List<Rank> workingAlt = new ArrayList<>();
|
||||
List<Rank> allRanks = SyncUtils.blockMulti(ranksCollection.find());
|
||||
allRanks.sort((a, b) -> Ints.compare(a.getWeight(), b.getWeight()));
|
||||
|
||||
for (Rank rank : APIv3.getDatastore().createQuery(Rank.class).order("weight").asList()) {
|
||||
for (Rank rank : allRanks) {
|
||||
working.put(rank.getId(), rank);
|
||||
workingAlt.add(rank);
|
||||
}
|
||||
@ -65,4 +74,22 @@ public final class Rank {
|
||||
}
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
ranksCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
ranksCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
|
||||
ranksCollection.deleteOne(new Document("_id", id), callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,27 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.serialization.ExcludeFromReplies;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Indexed;
|
||||
import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Entity(value = "servers", noClassnameStored = true)
|
||||
@Entity
|
||||
public final class Server {
|
||||
|
||||
private static final MongoCollection<Server> serversCollection = APIv3.getDatabase().getCollection("servers", Server.class);
|
||||
|
||||
private static Map<String, Server> serverCache = null;
|
||||
private static List<Server> serverCacheAlt = null;
|
||||
private static long serverCacheUpdated = 0;
|
||||
@ -21,22 +29,22 @@ public final class Server {
|
||||
@Getter @Id private String id;
|
||||
@Getter private String displayName;
|
||||
@Getter @ExcludeFromReplies String apiKey;
|
||||
@Getter @Indexed private String serverGroup;
|
||||
@Getter private String serverGroup;
|
||||
@Getter private String serverIp;
|
||||
@Getter @Setter private Date lastUpdatedAt;
|
||||
@Getter @Setter private double lastTps;
|
||||
@Getter @Setter @ExcludeFromReplies private Set<UUID> players;
|
||||
|
||||
public static Server byId(String id) {
|
||||
updateCacheIfNeeded();
|
||||
return serverCache.get(id);
|
||||
}
|
||||
|
||||
public static List<Server> values() {
|
||||
public static List<Server> findAll() {
|
||||
updateCacheIfNeeded();
|
||||
return serverCacheAlt;
|
||||
}
|
||||
|
||||
public static Server findById(String id) {
|
||||
updateCacheIfNeeded();
|
||||
return serverCache.get(id);
|
||||
}
|
||||
|
||||
public Server() {} // For Morphia
|
||||
|
||||
public Server(String id, String displayName, String apiKey, ServerGroup serverGroup, String serverIp) {
|
||||
@ -50,16 +58,12 @@ public final class Server {
|
||||
this.players = new HashSet<>();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
APIv3.getDatastore().delete(this);
|
||||
}
|
||||
|
||||
private static void updateCacheIfNeeded() {
|
||||
if (serverCache == null || (System.currentTimeMillis() - serverCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) {
|
||||
Map<String, Server> working = new HashMap<>();
|
||||
List<Server> workingAlt = new ArrayList<>();
|
||||
|
||||
for (Server server : APIv3.getDatastore().createQuery(Server.class).asList()) {
|
||||
for (Server server : SyncUtils.blockMulti(serversCollection.find())) {
|
||||
working.put(server.getId(), server);
|
||||
workingAlt.add(server);
|
||||
}
|
||||
@ -70,4 +74,28 @@ public final class Server {
|
||||
}
|
||||
}
|
||||
|
||||
public void receivedHeartbeat(double tps, Iterable<UUID> players) {
|
||||
this.lastUpdatedAt = new Date();
|
||||
this.lastTps = tps;
|
||||
this.players = ImmutableSet.copyOf(players);
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
serversCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
serversCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
|
||||
serversCollection.deleteOne(new Document("_id", id), callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
@ -1,66 +1,66 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.serialization.ExcludeFromReplies;
|
||||
import net.frozenorb.apiv3.utils.PermissionUtils;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Property;
|
||||
import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.PermissionUtils;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Entity(value = "serverGroups", noClassnameStored = true)
|
||||
@Entity
|
||||
public final class ServerGroup {
|
||||
|
||||
private static final MongoCollection<ServerGroup> serverGroupsCollection = APIv3.getDatabase().getCollection("serverGroups", ServerGroup.class);
|
||||
|
||||
private static Map<String, ServerGroup> serverGroupCache = null;
|
||||
private static List<ServerGroup> serverGroupAltCache = null;
|
||||
private static long serverGroupCacheUpdated = 0;
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private String image;
|
||||
// We rename this to public, we just can't name it that because it's a Java identifier.
|
||||
@Getter @Property("public") @SerializedName("public") private boolean isPublic;
|
||||
// We define these HashSets up here because, in the event they're
|
||||
// empty, Morphia will load them as null, not empty sets.
|
||||
@Getter @Setter @ExcludeFromReplies private Set<String> announcements = new HashSet<>();
|
||||
@Getter @Setter @ExcludeFromReplies private Map<String, List<String>> permissions = new HashMap<>();
|
||||
|
||||
public static ServerGroup byId(String id) {
|
||||
updateCacheIfNeeded();
|
||||
return serverGroupCache.get(id);
|
||||
}
|
||||
|
||||
public static List<ServerGroup> values() {
|
||||
// make this and other stuff async
|
||||
public static List<ServerGroup> findAll() {
|
||||
updateCacheIfNeeded();
|
||||
return serverGroupAltCache;
|
||||
}
|
||||
|
||||
public static ServerGroup findById(String id) {
|
||||
updateCacheIfNeeded();
|
||||
return serverGroupCache.get(id);
|
||||
}
|
||||
|
||||
public ServerGroup() {} // For Morphia
|
||||
|
||||
public ServerGroup(String id, String image, boolean isPublic) {
|
||||
public ServerGroup(String id, String image) {
|
||||
this.id = id;
|
||||
this.image = image;
|
||||
this.isPublic = isPublic;
|
||||
}
|
||||
|
||||
public Map<String, Boolean> calculatePermissions(Rank userRank) {
|
||||
return PermissionUtils.mergeUpTo(permissions, userRank);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
APIv3.getDatastore().delete(this);
|
||||
}
|
||||
|
||||
private static void updateCacheIfNeeded() {
|
||||
if (serverGroupCache == null || (System.currentTimeMillis() - serverGroupCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) {
|
||||
Map<String, ServerGroup> working = new HashMap<>();
|
||||
List<ServerGroup> workingAlt = new ArrayList<>();
|
||||
|
||||
for (ServerGroup serverGroup : APIv3.getDatastore().createQuery(ServerGroup.class).asList()) {
|
||||
for (ServerGroup serverGroup : SyncUtils.blockMulti(serverGroupsCollection.find())) {
|
||||
working.put(serverGroup.getId(), serverGroup);
|
||||
workingAlt.add(serverGroup);
|
||||
}
|
||||
@ -71,4 +71,22 @@ public final class ServerGroup {
|
||||
}
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
serverGroupsCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
serverGroupsCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
|
||||
serverGroupsCollection.deleteOne(new Document("_id", id), callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
417
src/main/java/net/frozenorb/apiv3/model/User.java
Normal file
417
src/main/java/net/frozenorb/apiv3/model/User.java
Normal file
@ -0,0 +1,417 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import io.vertx.core.CompositeFuture;
|
||||
import io.vertx.core.Future;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.serialization.gson.ExcludeFromReplies;
|
||||
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonDeserializer;
|
||||
import net.frozenorb.apiv3.serialization.jackson.UUIDJsonSerializer;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.MojangUtils;
|
||||
import net.frozenorb.apiv3.util.PermissionUtils;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import net.frozenorb.apiv3.util.UUIDUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
public final class User {
|
||||
|
||||
private static final MongoCollection<User> usersCollection = APIv3.getDatabase().getCollection("users", User.class);
|
||||
|
||||
@Getter @Id @JsonSerialize(using=UUIDJsonSerializer.class) @JsonDeserialize(using=UUIDJsonDeserializer.class) private UUID id;
|
||||
@Getter private String lastUsername;
|
||||
@Getter @ExcludeFromReplies private Map<String, Date> aliases = new HashMap<>();
|
||||
@Getter @ExcludeFromReplies @Setter private String totpSecret;
|
||||
@Getter @ExcludeFromReplies @Setter private String emailToken;
|
||||
@Getter @ExcludeFromReplies @Setter private Date emailTokenSetAt;
|
||||
@Getter @ExcludeFromReplies private String password;
|
||||
@Getter @Setter private String email;
|
||||
@Getter private String phoneNumber;
|
||||
@Getter private String lastSeenOn;
|
||||
@Getter private Date lastSeenAt;
|
||||
@Getter private Date firstSeenAt;
|
||||
@Getter private boolean online;
|
||||
|
||||
public static List<User> findAllSync() {
|
||||
return SyncUtils.blockMulti(usersCollection.find().sort(new Document("lastSeenAt", -1)));
|
||||
}
|
||||
|
||||
public static User findByIdSync(String id) {
|
||||
UUID uuid;
|
||||
|
||||
try {
|
||||
uuid = UUID.fromString(id);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return findByIdSync(uuid);
|
||||
}
|
||||
|
||||
public static User findByIdSync(UUID id) {
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
return SyncUtils.blockOne(usersCollection.find(new Document("_id", id)));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static User findByEmailTokenSync(String emailToken) {
|
||||
return SyncUtils.blockOne(usersCollection.find(new Document("emailToken", emailToken)));
|
||||
}
|
||||
|
||||
public static User findByLastUsernameSync(String lastUsername) {
|
||||
return SyncUtils.blockOne(usersCollection.find(new Document("lastUsername", lastUsername)));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<User>> callback) {
|
||||
usersCollection.find().sort(new Document("lastSeenAt", -1)).into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<User> callback) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(id);
|
||||
findById(uuid, callback);
|
||||
} catch (IllegalArgumentException ex) { // from UUID parsing
|
||||
callback.onResult(null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void findById(UUID id, SingleResultCallback<User> callback) {
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
usersCollection.find(new Document("_id", id)).first(callback);
|
||||
} else {
|
||||
callback.onResult(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void findByIdGrouped(Iterable<UUID> search, SingleResultCallback<Map<UUID, User>> callback) {
|
||||
usersCollection.find(new Document("_id", new Document("$in", search))).into(new ArrayList<>(), (users, error) -> {
|
||||
if (error != null) {
|
||||
callback.onResult(null, error);
|
||||
} else {
|
||||
Map<UUID, User> result = new HashMap<>();
|
||||
|
||||
for (UUID user : search) {
|
||||
result.put(user, null);
|
||||
}
|
||||
|
||||
for (User user : users) {
|
||||
result.put(user.getId(), user);
|
||||
}
|
||||
|
||||
callback.onResult(result, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void findByEmailToken(String emailToken, SingleResultCallback<User> callback) {
|
||||
usersCollection.find(new Document("emailToken", emailToken)).first(callback);
|
||||
}
|
||||
|
||||
public static void findByLastUsername(String lastUsername, SingleResultCallback<User> callback) {
|
||||
usersCollection.find(new Document("lastUsername", lastUsername)).first(callback);
|
||||
}
|
||||
|
||||
public User() {} // For Morphia
|
||||
|
||||
public User(UUID id, String lastUsername) {
|
||||
this.id = id;
|
||||
this.lastUsername = ""; // Intentional, so updateUsername actually does something.
|
||||
this.aliases = new HashMap<>();
|
||||
this.totpSecret = null;
|
||||
this.password = null;
|
||||
this.email = null;
|
||||
this.phoneNumber = null;
|
||||
this.lastSeenOn = null;
|
||||
this.lastSeenAt = new Date();
|
||||
this.firstSeenAt = new Date();
|
||||
|
||||
// TODO: MAKE THIS ASYNC? SOMEHOW?
|
||||
BlockingCallback<Void> blockingCallback = new BlockingCallback<>();
|
||||
updateUsername(lastUsername, blockingCallback);
|
||||
blockingCallback.get();
|
||||
}
|
||||
|
||||
public boolean hasPermissionAnywhere(String permission) {
|
||||
Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRankAnywhere());
|
||||
|
||||
for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks().entrySet()) {
|
||||
ServerGroup serverGroup = serverGroupEntry.getKey();
|
||||
Rank rank = serverGroupEntry.getValue();
|
||||
|
||||
globalPermissions = PermissionUtils.mergePermissions(
|
||||
globalPermissions,
|
||||
serverGroup.calculatePermissions(rank)
|
||||
);
|
||||
}
|
||||
|
||||
return globalPermissions.containsKey(permission) && globalPermissions.get(permission);
|
||||
}
|
||||
|
||||
// TODO: Clean
|
||||
public boolean seenOnServer(Server server) {
|
||||
if (online && server.getId().equals(this.lastSeenOn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.lastSeenOn = server.getId();
|
||||
|
||||
if (!online) {
|
||||
this.lastSeenAt = new Date();
|
||||
}
|
||||
|
||||
this.online = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void leftServer() {
|
||||
this.lastSeenAt = new Date();
|
||||
this.online = false;
|
||||
}
|
||||
|
||||
public void updateUsername(String newUsername, SingleResultCallback<Void> callback) {
|
||||
this.aliases.put(newUsername, new Date());
|
||||
|
||||
if (newUsername.equalsIgnoreCase(lastUsername)) {
|
||||
callback.onResult(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastUsername = newUsername;
|
||||
|
||||
User.findByLastUsername(newUsername, (otherUser, error) -> {
|
||||
if (error != null) {
|
||||
callback.onResult(null, error);
|
||||
} else if (otherUser != null) {
|
||||
MojangUtils.getName(otherUser.getId(), (newName, error2) -> {
|
||||
if (error2 != null) {
|
||||
callback.onResult(null, error2);
|
||||
} else {
|
||||
otherUser.updateUsername(newName, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.onResult(null, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setPassword(String input) {
|
||||
this.password = Hashing
|
||||
.sha256()
|
||||
.hashString(input + "$" + id.toString(), Charsets.UTF_8)
|
||||
.toString();
|
||||
}
|
||||
|
||||
public boolean checkPassword(String input) {
|
||||
String hashed = Hashing
|
||||
.sha256()
|
||||
.hashString(input + "$" + id.toString(), Charsets.UTF_8)
|
||||
.toString();
|
||||
|
||||
return password != null && hashed.equals(password);
|
||||
}
|
||||
|
||||
public Rank getHighestRankAnywhere() {
|
||||
return getHighestRankScoped(null);
|
||||
}
|
||||
|
||||
public Rank getHighestRankScoped(ServerGroup serverGroup) {
|
||||
return getHighestRankScoped(serverGroup, Grant.findByUserSync(this));
|
||||
}
|
||||
|
||||
// TODO: Clean
|
||||
// This is only used to help batch requests to mongo
|
||||
public Rank getHighestRankScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
|
||||
Rank highest = null;
|
||||
|
||||
for (Grant grant : grants) {
|
||||
if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rank rank = Rank.findById(grant.getRank());
|
||||
|
||||
if (highest == null || rank.getWeight() > highest.getWeight()) {
|
||||
highest = rank;
|
||||
}
|
||||
}
|
||||
|
||||
if (highest != null) {
|
||||
return highest;
|
||||
} else {
|
||||
return Rank.findById("default");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Clean
|
||||
public Map<ServerGroup, Rank> getHighestRanks() {
|
||||
Map<ServerGroup, Rank> highestRanks = new HashMap<>();
|
||||
Rank defaultRank = Rank.findById("default");
|
||||
List<Grant> userGrants = Grant.findByUserSync(this);
|
||||
|
||||
for (ServerGroup serverGroup : ServerGroup.findAll()) {
|
||||
Rank highest = defaultRank;
|
||||
|
||||
for (Grant grant : userGrants) {
|
||||
if (!grant.isActive() || !grant.appliesOn(serverGroup)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rank rank = Rank.findById(grant.getRank());
|
||||
|
||||
if (highest == null || rank.getWeight() > highest.getWeight()) {
|
||||
highest = rank;
|
||||
}
|
||||
}
|
||||
|
||||
highestRanks.put(serverGroup, highest);
|
||||
}
|
||||
|
||||
return highestRanks;
|
||||
}
|
||||
|
||||
public void getLoginInfo(Server server, String userIp, SingleResultCallback<Map<String, Object>> callback) {
|
||||
Future<Iterable<Punishment>> punishmentsFuture = Future.future();
|
||||
Future<Iterable<IpBan>> ipBansFuture = Future.future();
|
||||
Future<Iterable<Grant>> grantsFuture = Future.future();
|
||||
|
||||
Punishment.findByUserAndType(this, ImmutableSet.of(
|
||||
Punishment.PunishmentType.BLACKLIST,
|
||||
Punishment.PunishmentType.BAN,
|
||||
Punishment.PunishmentType.MUTE
|
||||
), (punishments, error) -> {
|
||||
if (error != null) {
|
||||
punishmentsFuture.fail(error);
|
||||
} else {
|
||||
punishmentsFuture.complete(punishments);
|
||||
}
|
||||
});
|
||||
|
||||
if (userIp != null) {
|
||||
IpBan.findByIp(userIp, (ipBans, error) -> {
|
||||
if (error != null) {
|
||||
ipBansFuture.fail(error);
|
||||
} else {
|
||||
ipBansFuture.complete(ipBans);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ipBansFuture.complete(ImmutableSet.of());
|
||||
}
|
||||
|
||||
Grant.findByUser(this, (grants, error) -> {
|
||||
if (error != null) {
|
||||
grantsFuture.fail(error);
|
||||
} else {
|
||||
grantsFuture.complete(grants);
|
||||
}
|
||||
});
|
||||
|
||||
CompositeFuture.all(punishmentsFuture, ipBansFuture, grantsFuture).setHandler((result) -> {
|
||||
if (result.succeeded()) {
|
||||
Iterable<Punishment> punishments = result.result().result(0);
|
||||
Iterable<IpBan> ipBans = result.result().result(1);
|
||||
Iterable<Grant> grants = result.result().result(2);
|
||||
|
||||
callback.onResult(createLoginInfo(server, punishments, ipBans, grants), null);
|
||||
} else {
|
||||
callback.onResult(null, result.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This is only used to help batch requests to mongo
|
||||
public Map<String, Object> createLoginInfo(Server server, Iterable<Punishment> punishments, Iterable<IpBan> ipBans, Iterable<Grant> grants) {
|
||||
Punishment activeMute = null;
|
||||
Punishment activeBan = null;
|
||||
IpBan activeIpBan = null;
|
||||
|
||||
for (Punishment punishment : punishments) {
|
||||
if (!punishment.isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (punishment.getType() == Punishment.PunishmentType.MUTE) {
|
||||
activeMute = punishment;
|
||||
} else if (punishment.getType() == Punishment.PunishmentType.BAN || punishment.getType() == Punishment.PunishmentType.BLACKLIST) {
|
||||
activeBan = punishment;
|
||||
}
|
||||
}
|
||||
|
||||
for (IpBan ipBan : ipBans) {
|
||||
if (ipBan.isActive()) {
|
||||
activeIpBan = ipBan;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Rank highestRank = getHighestRankScoped(ServerGroup.findById(server.getServerGroup()), grants);
|
||||
Map<String, Object> access = ImmutableMap.of(
|
||||
"allowed", true,
|
||||
"message", "Public server"
|
||||
);
|
||||
|
||||
if (activeBan != null) {
|
||||
access = ImmutableMap.of(
|
||||
"allowed", false,
|
||||
"message", activeBan.getAccessDenialReason(),
|
||||
"activeBanId", activeBan.getId()
|
||||
);
|
||||
} else if (activeIpBan != null) {
|
||||
access = ImmutableMap.of(
|
||||
"allowed", false,
|
||||
"message", activeIpBan.getAccessDenialReason(),
|
||||
"activeIpBanId", activeIpBan.getId()
|
||||
);
|
||||
}
|
||||
|
||||
// Generics are weird, yes we have to do this.
|
||||
ImmutableMap.Builder<String, Object> result = ImmutableMap.<String, Object>builder()
|
||||
.put("user", this)
|
||||
.put("access", access)
|
||||
.put("rank", highestRank.getId())
|
||||
.put("totpSetup", getTotpSecret() != null);
|
||||
|
||||
if (activeMute != null) {
|
||||
result.put("mute", activeMute);
|
||||
}
|
||||
|
||||
return result.build();
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
usersCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
save(callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save(SingleResultCallback<UpdateResult> callback) {
|
||||
usersCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
}
|
||||
|
||||
}
|
92
src/main/java/net/frozenorb/apiv3/model/UserMetaEntry.java
Normal file
92
src/main/java/net/frozenorb/apiv3/model/UserMetaEntry.java
Normal file
@ -0,0 +1,92 @@
|
||||
package net.frozenorb.apiv3.model;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.async.client.MongoCollection;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.SyncUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public final class UserMetaEntry {
|
||||
|
||||
private static final MongoCollection<UserMetaEntry> userMetaCollection = APIv3.getDatabase().getCollection("userMeta", UserMetaEntry.class);
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private UUID user;
|
||||
@Getter private String serverGroup;
|
||||
@Getter @Setter private Map<String, Object> data;
|
||||
|
||||
public static List<UserMetaEntry> findAllSync() {
|
||||
return SyncUtils.blockMulti(userMetaCollection.find());
|
||||
}
|
||||
|
||||
public static UserMetaEntry findByIdSync(String id) {
|
||||
return SyncUtils.blockOne(userMetaCollection.find(new Document("_id", id)));
|
||||
}
|
||||
|
||||
public static UserMetaEntry findByUserAndGroupSync(User user, ServerGroup serverGroup) {
|
||||
return findByUserAndGroupSync(user.getId(), serverGroup);
|
||||
}
|
||||
|
||||
public static UserMetaEntry findByUserAndGroupSync(UUID user, ServerGroup serverGroup) {
|
||||
return SyncUtils.blockOne(userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())));
|
||||
}
|
||||
|
||||
public static void findAll(SingleResultCallback<List<UserMetaEntry>> callback) {
|
||||
userMetaCollection.find().into(new ArrayList<>(), callback);
|
||||
}
|
||||
|
||||
public static void findById(String id, SingleResultCallback<UserMetaEntry> callback) {
|
||||
userMetaCollection.find(new Document("_id", id)).first(callback);
|
||||
}
|
||||
|
||||
public static void findByUserAndGroup(User user, ServerGroup serverGroup, SingleResultCallback<UserMetaEntry> callback) {
|
||||
findByUserAndGroup(user.getId(), serverGroup, callback);
|
||||
}
|
||||
|
||||
public static void findByUserAndGroup(UUID user, ServerGroup serverGroup, SingleResultCallback<UserMetaEntry> callback) {
|
||||
userMetaCollection.find(new Document("user", user).append("serverGroup", serverGroup.getId())).first(callback);
|
||||
}
|
||||
|
||||
public UserMetaEntry() {} // For Morphia
|
||||
|
||||
public UserMetaEntry(User user, ServerGroup serverGroup, Map<String, Object> data) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.serverGroup = serverGroup.getId();
|
||||
this.data = ImmutableMap.copyOf(data);
|
||||
}
|
||||
|
||||
public void insert() {
|
||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
||||
userMetaCollection.insertOne(this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
|
||||
userMetaCollection.replaceOne(new Document("_id", id), this, callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
|
||||
userMetaCollection.deleteOne(new Document("_id", id), callback);
|
||||
callback.get();
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.actors.ActorType;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Indexed;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity(value = "auditLog", noClassnameStored = true)
|
||||
public final class AuditLogEntry {
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter @Indexed private UUID user;
|
||||
@Getter private String userIp;
|
||||
@Getter @Indexed private Date performedAt;
|
||||
@Getter private String actorName;
|
||||
@Getter private ActorType actorType;
|
||||
@Getter private AuditLogActionType type;
|
||||
@Getter private Map<String, Object> metadata;
|
||||
|
||||
public AuditLogEntry() {} // For Morphia
|
||||
|
||||
public AuditLogEntry(User user, String userIp, Actor actor, AuditLogActionType type, Map<String, Object> metadata) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.userIp = userIp;
|
||||
this.performedAt = new Date();
|
||||
this.actorName = actor.getName();
|
||||
this.actorType = actor.getType();
|
||||
this.type = type;
|
||||
this.metadata = ImmutableMap.copyOf(metadata);
|
||||
}
|
||||
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.utils.UUIDUtils;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Indexed;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Entity(value = "grants", noClassnameStored = true)
|
||||
@AllArgsConstructor
|
||||
public final class Grant {
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter @Indexed private UUID user;
|
||||
@Getter private String reason;
|
||||
@Getter private Set<String> scopes = new HashSet<>(); // So on things w/o scopes we still load properly (Morphia drops empty sets)
|
||||
@Getter @Indexed private String rank;
|
||||
@Getter private Date expiresAt;
|
||||
|
||||
@Getter private UUID addedBy;
|
||||
@Getter @Indexed private Date addedAt;
|
||||
|
||||
@Getter private UUID removedBy;
|
||||
@Getter private Date removedAt;
|
||||
@Getter private String removalReason;
|
||||
|
||||
public static Grant byId(String id) {
|
||||
return APIv3.getDatastore().createQuery(Grant.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static Map<UUID, List<Grant>> byUserGrouped(Iterable<UUID> ids) {
|
||||
Map<UUID, List<Grant>> result = new HashMap<>();
|
||||
Set<UUID> uuidsToSearch = new HashSet<>();
|
||||
|
||||
for (UUID id : ids) {
|
||||
result.put(id, new ArrayList<>());
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
uuidsToSearch.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
APIv3.getDatastore().createQuery(Grant.class).field("user").in(uuidsToSearch).forEach((grant) -> {
|
||||
result.get(grant.getUser()).add(grant);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Grant() {} // For Morphia
|
||||
|
||||
public Grant(User user, String reason, Set<ServerGroup> scopes, Rank rank, Date expiresAt, User addedBy) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.reason = reason;
|
||||
this.scopes = new HashSet<>(Collections2.transform(scopes, ServerGroup::getId));
|
||||
this.rank = rank.getId();
|
||||
this.expiresAt = expiresAt;
|
||||
this.addedBy = addedBy == null ? null : addedBy.getId();
|
||||
this.addedAt = new Date();
|
||||
}
|
||||
|
||||
public void delete(User removedBy, String reason) {
|
||||
this.removedBy = removedBy.getId();
|
||||
this.removedAt = new Date();
|
||||
this.removalReason = reason;
|
||||
|
||||
APIv3.getDatastore().save(this);
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return !(isExpired() || isRemoved());
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
if (expiresAt == null) {
|
||||
return false; // Never expires
|
||||
} else {
|
||||
return expiresAt.before(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRemoved() {
|
||||
return removedBy != null;
|
||||
}
|
||||
|
||||
public boolean appliesOn(ServerGroup serverGroup) {
|
||||
return isGlobal() || scopes.contains(serverGroup.getId());
|
||||
}
|
||||
|
||||
public boolean isGlobal() {
|
||||
return scopes.isEmpty();
|
||||
}
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.annotations.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity(value = "ipLog", noClassnameStored = true)
|
||||
@AllArgsConstructor
|
||||
@Indexes(
|
||||
@Index(fields = {
|
||||
@Field("user"),
|
||||
@Field("userIp")
|
||||
})
|
||||
)
|
||||
public final class IPLogEntry {
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter @Indexed private UUID user;
|
||||
@Getter @Indexed private String userIp;
|
||||
@Getter private Date firstSeenAt;
|
||||
@Getter private Date lastSeenAt;
|
||||
@Getter private int uses;
|
||||
|
||||
public IPLogEntry() {} // For Morphia
|
||||
|
||||
public IPLogEntry(User user, String userIp) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.userIp = userIp;
|
||||
this.firstSeenAt = new Date();
|
||||
this.lastSeenAt = new Date();
|
||||
this.uses = 0;
|
||||
}
|
||||
|
||||
public void used() {
|
||||
this.lastSeenAt = new Date();
|
||||
this.uses++;
|
||||
|
||||
APIv3.getDatastore().save(this);
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Entity(value = "notificationTemplates", noClassnameStored = true)
|
||||
public final class NotificationTemplate {
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter private String subject;
|
||||
@Getter private String body;
|
||||
|
||||
public static NotificationTemplate byId(String id) {
|
||||
return APIv3.getDatastore().createQuery(NotificationTemplate.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static List<NotificationTemplate> values() {
|
||||
return APIv3.getDatastore().createQuery(NotificationTemplate.class).asList();
|
||||
}
|
||||
|
||||
public NotificationTemplate() {} // For Morphia
|
||||
|
||||
public NotificationTemplate(String id, String subject, String body) {
|
||||
this.id = id;
|
||||
this.subject = subject;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void update(String subject, String body) {
|
||||
this.subject = subject;
|
||||
this.body = body;
|
||||
|
||||
APIv3.getDatastore().save(this);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
APIv3.getDatastore().delete(this);
|
||||
}
|
||||
|
||||
public String fillSubject(Map<String, Object> replacements) {
|
||||
return fill(subject, replacements);
|
||||
}
|
||||
|
||||
public String fillBody(Map<String, Object> replacements) {
|
||||
return fill(body, replacements);
|
||||
}
|
||||
|
||||
private String fill(String working, Map<String, Object> replacements) {
|
||||
for (Map.Entry<String, Object> replacement : replacements.entrySet()) {
|
||||
String key = replacement.getKey();
|
||||
String value = String.valueOf(replacement.getValue());
|
||||
|
||||
working = working.replace("%" + key + "%", value);
|
||||
}
|
||||
|
||||
return working;
|
||||
}
|
||||
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.actors.ActorType;
|
||||
import net.frozenorb.apiv3.utils.TimeUtils;
|
||||
import net.frozenorb.apiv3.utils.UUIDUtils;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.annotations.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Entity(value = "punishments", noClassnameStored = true)
|
||||
@AllArgsConstructor
|
||||
@Indexes(
|
||||
@Index(fields = {
|
||||
@Field("user"),
|
||||
@Field("type")
|
||||
})
|
||||
)
|
||||
public final class Punishment {
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter @Indexed private UUID user;
|
||||
@Getter private String reason;
|
||||
@Getter @Indexed private PunishmentType type; // Type is indexed for the rank dump
|
||||
@Getter private Date expiresAt;
|
||||
@Getter private Map<String, Object> metadata;
|
||||
|
||||
@Getter private UUID addedBy;
|
||||
@Getter @Indexed private Date addedAt;
|
||||
@Getter private String actorName;
|
||||
@Getter private ActorType actorType;
|
||||
|
||||
@Getter private UUID removedBy;
|
||||
@Getter private Date removedAt;
|
||||
@Getter private String removalReason;
|
||||
|
||||
public static Punishment byId(String id) {
|
||||
return APIv3.getDatastore().createQuery(Punishment.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static Map<UUID, List<Punishment>> byUserGrouped(Iterable<UUID> ids) {
|
||||
return byUserGrouped(ids, ImmutableSet.copyOf(PunishmentType.values()));
|
||||
}
|
||||
|
||||
public static Map<UUID, List<Punishment>> byUserGrouped(Iterable<UUID> ids, Iterable<Punishment.PunishmentType> types) {
|
||||
Map<UUID, List<Punishment>> result = new HashMap<>();
|
||||
Set<UUID> uuidsToSearch = new HashSet<>();
|
||||
|
||||
for (UUID id : ids) {
|
||||
result.put(id, new ArrayList<>());
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
uuidsToSearch.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
APIv3.getDatastore().createQuery(Punishment.class).field("user").in(uuidsToSearch).field("type").in(types).forEach((punishment) -> {
|
||||
result.get(punishment.getUser()).add(punishment);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Punishment() {} // For Morphia
|
||||
|
||||
public Punishment(User user, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map<String, Object> metadata) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.reason = reason;
|
||||
this.type = type;
|
||||
this.expiresAt = expiresAt;
|
||||
this.addedBy = addedBy == null ? null : addedBy.getId();
|
||||
this.addedAt = new Date();
|
||||
this.actorName = actor.getName();
|
||||
this.actorType = actor.getType();
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public void delete(User removedBy, String reason) {
|
||||
this.removedBy = removedBy.getId();
|
||||
this.removedAt = new Date();
|
||||
this.removalReason = reason;
|
||||
|
||||
APIv3.getDatastore().save(this);
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return !(isExpired() || isRemoved());
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
if (expiresAt == null) {
|
||||
return false; // Never expires
|
||||
} else {
|
||||
return expiresAt.before(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRemoved() {
|
||||
return removedBy != null;
|
||||
}
|
||||
|
||||
public String getAccessDenialReason() {
|
||||
switch (type) {
|
||||
case BLACKLIST:
|
||||
return "Your account has been blacklisted from the MineHQ Network. \n\nThis type of punishment cannot be appealed.";
|
||||
case BAN:
|
||||
String accessDenialReason = "Your account has been suspended from the MineHQ Network. \n\n";
|
||||
|
||||
if (getExpiresAt() != null) {
|
||||
accessDenialReason += "Expires in " + TimeUtils.formatIntoDetailedString(TimeUtils.getSecondsBetween(getExpiresAt(), new Date()));
|
||||
} else {
|
||||
accessDenialReason += "Appeal at MineHQ.com/appeal";
|
||||
}
|
||||
|
||||
return accessDenialReason;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum PunishmentType {
|
||||
|
||||
BLACKLIST, BAN, MUTE, WARN
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.hash.Hashing;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.serialization.ExcludeFromReplies;
|
||||
import net.frozenorb.apiv3.utils.MojangUtils;
|
||||
import net.frozenorb.apiv3.utils.PermissionUtils;
|
||||
import net.frozenorb.apiv3.utils.UUIDUtils;
|
||||
import org.bson.Document;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Indexed;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Entity(value = "users", noClassnameStored = true)
|
||||
@AllArgsConstructor
|
||||
public final class User {
|
||||
|
||||
@Getter @Id private UUID id;
|
||||
@Getter @Indexed private String lastUsername;
|
||||
@Getter @ExcludeFromReplies private Map<String, Date> aliases = new HashMap<>();
|
||||
@Getter @Setter @ExcludeFromReplies private String totpSecret;
|
||||
@Getter @Indexed @ExcludeFromReplies @Setter private String emailToken;
|
||||
@Getter @ExcludeFromReplies @Setter private Date emailTokenSetAt;
|
||||
@Getter @ExcludeFromReplies private String password;
|
||||
@Getter @Setter private String email;
|
||||
@Getter private String phoneNumber;
|
||||
@Getter private String lastSeenOn;
|
||||
@Getter private Date lastSeenAt;
|
||||
@Getter private Date firstSeenAt;
|
||||
@Getter private boolean online;
|
||||
|
||||
public static User byId(String id) {
|
||||
try {
|
||||
return byId(UUID.fromString(id));
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static User byId(UUID id) {
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
return APIv3.getDatastore().createQuery(User.class).field("id").equal(id).get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<UUID, User> byIdGrouped(Iterable<UUID> ids) {
|
||||
Map<UUID, User> result = new HashMap<>();
|
||||
Set<UUID> uuidsToSearch = new HashSet<>();
|
||||
|
||||
for (UUID id : ids) {
|
||||
result.put(id, null);
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
uuidsToSearch.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
APIv3.getDatastore().createQuery(User.class).field("id").in(uuidsToSearch).forEach((user) -> {
|
||||
result.put(user.getId(), user);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static User byEmailToken(String name) {
|
||||
return APIv3.getDatastore().createQuery(User.class).field("emailToken").equal(name).get();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static User byLastUsername(String lastUsername) {
|
||||
return APIv3.getDatastore().createQuery(User.class).field("lastUsername").equal(lastUsername).get();
|
||||
}
|
||||
|
||||
public User() {} // For Morphia
|
||||
|
||||
public User(UUID id, String lastUsername) {
|
||||
this.id = id;
|
||||
this.lastUsername = ""; // Intentional, so updateUsername actually does something.
|
||||
this.aliases = new HashMap<>();
|
||||
this.totpSecret = null;
|
||||
this.password = null;
|
||||
this.email = null;
|
||||
this.phoneNumber = null;
|
||||
this.lastSeenOn = null;
|
||||
this.lastSeenAt = new Date();
|
||||
this.firstSeenAt = new Date();
|
||||
|
||||
updateUsername(lastUsername);
|
||||
}
|
||||
|
||||
public boolean hasPermissionScoped(String permission, ServerGroup scope) {
|
||||
Rank highestRank = getHighestRank(scope);
|
||||
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
|
||||
PermissionUtils.getDefaultPermissions(highestRank),
|
||||
scope.calculatePermissions(highestRank)
|
||||
);
|
||||
|
||||
return scopedPermissions.containsKey(permission) && scopedPermissions.get(permission);
|
||||
}
|
||||
|
||||
public boolean hasPermissionAnywhere(String permission) {
|
||||
Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRank());
|
||||
|
||||
for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks().entrySet()) {
|
||||
ServerGroup serverGroup = serverGroupEntry.getKey();
|
||||
Rank rank = serverGroupEntry.getValue();
|
||||
|
||||
globalPermissions = PermissionUtils.mergePermissions(
|
||||
globalPermissions,
|
||||
serverGroup.calculatePermissions(rank)
|
||||
);
|
||||
}
|
||||
|
||||
return globalPermissions.containsKey(permission) && globalPermissions.get(permission);
|
||||
}
|
||||
|
||||
public List<Grant> getGrants() {
|
||||
return APIv3.getDatastore().createQuery(Grant.class).field("user").equal(id).asList();
|
||||
}
|
||||
|
||||
public List<IPLogEntry> getIPLog() {
|
||||
return APIv3.getDatastore().createQuery(IPLogEntry.class).field("user").equal(id).asList();
|
||||
}
|
||||
|
||||
public IPLogEntry getIPLogEntry(String ip) {
|
||||
IPLogEntry existing = APIv3.getDatastore().createQuery(IPLogEntry.class).field("user").equal(id).field("userIp").equal(ip).get();
|
||||
|
||||
if (existing == null) {
|
||||
existing = new IPLogEntry(this, ip);
|
||||
APIv3.getDatastore().save(existing);
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public List<Punishment> getPunishments() {
|
||||
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).asList();
|
||||
}
|
||||
|
||||
public List<Punishment> getPunishments(Iterable<Punishment.PunishmentType> types) {
|
||||
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).field("type").in(types).asList();
|
||||
}
|
||||
|
||||
public UserMetaEntry getMeta(ServerGroup group) {
|
||||
return APIv3.getDatastore().createQuery(UserMetaEntry.class).field("user").equal(id).field("serverGroup").equal(group.getId()).get();
|
||||
}
|
||||
|
||||
public void saveMeta(ServerGroup group, Document data) {
|
||||
UserMetaEntry entry = getMeta(group);
|
||||
|
||||
if (entry == null) {
|
||||
APIv3.getDatastore().save(new UserMetaEntry(this, group, data));
|
||||
} else {
|
||||
entry.setData(data);
|
||||
APIv3.getDatastore().save(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean seenOnServer(Server server) {
|
||||
if (online && server.getId().equals(this.lastSeenOn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.lastSeenOn = server.getId();
|
||||
|
||||
if (!online) {
|
||||
this.lastSeenAt = new Date();
|
||||
}
|
||||
|
||||
this.online = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void leftServer() {
|
||||
this.lastSeenAt = new Date();
|
||||
this.online = false;
|
||||
}
|
||||
|
||||
public void updateUsername(String username) {
|
||||
if (!username.equals(lastUsername)) {
|
||||
this.lastUsername = username;
|
||||
|
||||
User withNewUsername;
|
||||
|
||||
while ((withNewUsername = User.byLastUsername(username)) != null) {
|
||||
String newUsername = MojangUtils.getName(withNewUsername.getId());
|
||||
withNewUsername.updateUsername(newUsername);
|
||||
}
|
||||
}
|
||||
|
||||
this.aliases.put(username, new Date());
|
||||
APIv3.getDatastore().save(this);
|
||||
}
|
||||
|
||||
public void setPassword(String input) {
|
||||
this.password = Hashing
|
||||
.sha256()
|
||||
.hashString(input + "$" + id.toString(), Charsets.UTF_8)
|
||||
.toString();
|
||||
}
|
||||
|
||||
public boolean checkPassword(String input) {
|
||||
String hashed = Hashing
|
||||
.sha256()
|
||||
.hashString(input + "$" + id.toString(), Charsets.UTF_8)
|
||||
.toString();
|
||||
|
||||
return hashed.equals(password);
|
||||
}
|
||||
|
||||
public Rank getHighestRank() {
|
||||
return getHighestRank(null);
|
||||
}
|
||||
|
||||
public Rank getHighestRank(ServerGroup serverGroup) {
|
||||
return getHighestRank(serverGroup, getGrants());
|
||||
}
|
||||
|
||||
// This is only used to help batch requests to mongo
|
||||
public Rank getHighestRank(ServerGroup serverGroup, Iterable<Grant> grants) {
|
||||
Rank highest = null;
|
||||
|
||||
for (Grant grant : grants) {
|
||||
if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rank rank = Rank.byId(grant.getRank());
|
||||
|
||||
if (highest == null || rank.getWeight() > highest.getWeight()) {
|
||||
highest = rank;
|
||||
}
|
||||
}
|
||||
|
||||
if (highest != null) {
|
||||
return highest;
|
||||
} else {
|
||||
return Rank.byId("default");
|
||||
}
|
||||
}
|
||||
|
||||
public Map<ServerGroup, Rank> getHighestRanks() {
|
||||
Map<ServerGroup, Rank> highestRanks = new HashMap<>();
|
||||
Rank defaultRank = Rank.byId("default");
|
||||
List<Grant> userGrants = getGrants();
|
||||
|
||||
for (ServerGroup serverGroup : ServerGroup.values()) {
|
||||
Rank highest = defaultRank;
|
||||
|
||||
for (Grant grant : userGrants) {
|
||||
if (!grant.isActive() || !grant.appliesOn(serverGroup)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rank rank = Rank.byId(grant.getRank());
|
||||
|
||||
if (highest == null || rank.getWeight() > highest.getWeight()) {
|
||||
highest = rank;
|
||||
}
|
||||
}
|
||||
|
||||
highestRanks.put(serverGroup, highest);
|
||||
}
|
||||
|
||||
return highestRanks;
|
||||
}
|
||||
|
||||
public Map<String, Object> getLoginInfo(Server server) {
|
||||
return getLoginInfo(
|
||||
server,
|
||||
getPunishments(ImmutableSet.of(
|
||||
Punishment.PunishmentType.BLACKLIST,
|
||||
Punishment.PunishmentType.BAN,
|
||||
Punishment.PunishmentType.MUTE
|
||||
)),
|
||||
getGrants()
|
||||
);
|
||||
}
|
||||
|
||||
// This is only used to help batch requests to mongo
|
||||
public Map<String, Object> getLoginInfo(Server server, Iterable<Punishment> punishments, Iterable<Grant> grants) {
|
||||
Punishment activeMute = null;
|
||||
String accessDenialReason = null;
|
||||
|
||||
for (Punishment punishment : punishments) {
|
||||
if (!punishment.isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (punishment.getType() == Punishment.PunishmentType.MUTE) {
|
||||
activeMute = punishment;
|
||||
} else {
|
||||
accessDenialReason = punishment.getAccessDenialReason();
|
||||
}
|
||||
}
|
||||
|
||||
Rank highestRank = getHighestRank(ServerGroup.byId(server.getServerGroup()), grants);
|
||||
|
||||
// Generics are weird, yes we have to do this.
|
||||
ImmutableMap.Builder<String, Object> result = ImmutableMap.<String, Object>builder()
|
||||
.put("user", this)
|
||||
.put("access", ImmutableMap.of(
|
||||
"allowed", accessDenialReason == null,
|
||||
"message", accessDenialReason == null ? "Public server" : accessDenialReason
|
||||
))
|
||||
.put("rank", highestRank.getId())
|
||||
.put("totpSetup", getTotpSecret() != null);
|
||||
|
||||
if (activeMute != null) {
|
||||
result.put("mute", activeMute);
|
||||
}
|
||||
|
||||
return result.build();
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Indexed;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity(value = "userMeta", noClassnameStored = true)
|
||||
public final class UserMetaEntry {
|
||||
|
||||
@Getter @Id private String id;
|
||||
@Getter @Indexed private UUID user;
|
||||
@Getter @Indexed private String serverGroup;
|
||||
@Getter @Setter private Map<String, Object> data;
|
||||
|
||||
public UserMetaEntry() {} // For Morphia
|
||||
|
||||
public UserMetaEntry(User user, ServerGroup serverGroup, Map<String, Object> data) {
|
||||
this.id = new ObjectId().toString();
|
||||
this.user = user.getId();
|
||||
this.serverGroup = serverGroup.getId();
|
||||
this.data = ImmutableMap.copyOf(data);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
APIv3.getDatastore().delete(this);
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +1,24 @@
|
||||
package net.frozenorb.apiv3.routes;
|
||||
package net.frozenorb.apiv3.route;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.timgroup.statsd.Event;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.models.Grant;
|
||||
import net.frozenorb.apiv3.models.Punishment;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class GETDump implements Route {
|
||||
public final class GETDump implements Handler<RoutingContext> {
|
||||
|
||||
private List<UUID> banCache = new ArrayList<>();
|
||||
private List<UUID> blacklistCache = new ArrayList<>();
|
||||
private Map<String, List<UUID>> grantCache = new HashMap<>();
|
||||
|
||||
public GETDump() {
|
||||
// TODO: Use vertx scheduler
|
||||
Thread dumpUpdater = new Thread() {
|
||||
|
||||
@Override
|
||||
@ -27,11 +26,11 @@ public final class GETDump implements Route {
|
||||
int tick = 0;
|
||||
|
||||
while (true) {
|
||||
if (tick == 0 || tick % 2 == 1) {
|
||||
if (tick == 0 || tick % 2 != 0) {
|
||||
List<UUID> banCache = new ArrayList<>();
|
||||
List<UUID> blacklistCache = new ArrayList<>();
|
||||
|
||||
APIv3.getDatastore().createQuery(Punishment.class).field("type").in(ImmutableSet.of(
|
||||
Punishment.findByTypeSync(ImmutableSet.of(
|
||||
Punishment.PunishmentType.BAN,
|
||||
Punishment.PunishmentType.BLACKLIST
|
||||
)).forEach((punishment) -> {
|
||||
@ -53,7 +52,7 @@ public final class GETDump implements Route {
|
||||
if (tick == 0 || tick % 2 == 0) {
|
||||
Map<String, List<UUID>> grantCache = new HashMap<>();
|
||||
|
||||
APIv3.getDatastore().createQuery(Grant.class).forEach((grant) -> {
|
||||
Grant.findAllSync().forEach((grant) -> {
|
||||
if (grant.isActive()) {
|
||||
List<UUID> users = grantCache.get(grant.getRank());
|
||||
|
||||
@ -71,7 +70,7 @@ public final class GETDump implements Route {
|
||||
|
||||
try {
|
||||
Thread.sleep(TimeUnit.MINUTES.toMillis(3));
|
||||
} catch (Exception ex) {
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
@ -86,25 +85,30 @@ public final class GETDump implements Route {
|
||||
dumpUpdater.start();
|
||||
}
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
String type = req.params("type");
|
||||
public void handle(RoutingContext ctx) {
|
||||
String type = ctx.request().getParam("type");
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case "ban":
|
||||
return banCache;
|
||||
APIv3.respondJson(ctx, banCache);
|
||||
return;
|
||||
case "blacklist":
|
||||
return blacklistCache;
|
||||
APIv3.respondJson(ctx, blacklistCache);
|
||||
return;
|
||||
case "accessdeniable": // Lowercase d because we convert to lowercase above
|
||||
List<UUID> result = new ArrayList<>();
|
||||
|
||||
result.addAll(banCache);
|
||||
result.addAll(blacklistCache);
|
||||
|
||||
return result;
|
||||
APIv3.respondJson(ctx, result);
|
||||
return;
|
||||
case "grant":
|
||||
return grantCache;
|
||||
APIv3.respondJson(ctx, grantCache);
|
||||
return;
|
||||
default:
|
||||
return ErrorUtils.invalidInput(type + " is not a valid type. Not in [ban, blacklist, accessDeniable, grant]");
|
||||
ErrorUtils.respondInvalidInput(ctx, type + " is not a valid type. Not in [ban, blacklist, accessDeniable, grant]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
21
src/main/java/net/frozenorb/apiv3/route/GETWhoAmI.java
Normal file
21
src/main/java/net/frozenorb/apiv3/route/GETWhoAmI.java
Normal file
@ -0,0 +1,21 @@
|
||||
package net.frozenorb.apiv3.route;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
|
||||
public final class GETWhoAmI implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
Actor actor = ctx.get("actor");
|
||||
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"name", actor.getName(),
|
||||
"type", actor.getType(),
|
||||
"authorized", actor.isAuthorized()
|
||||
));
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/net/frozenorb/apiv3/route/POSTMetrics.java
Normal file
14
src/main/java/net/frozenorb/apiv3/route/POSTMetrics.java
Normal file
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
|
||||
public final class POSTMetrics implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, ImmutableMap.of());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.frozenorb.apiv3.route.announcements;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETAnnouncements implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("id"));
|
||||
|
||||
if (serverGroup == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
APIv3.respondJson(ctx, serverGroup.getAnnouncements());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package net.frozenorb.apiv3.route.announcements;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class PUTAnnouncements implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("id"));
|
||||
|
||||
if (serverGroup == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> announcements = new HashSet<>();
|
||||
|
||||
for (Object announcement : ctx.getBodyAsJsonArray()) {
|
||||
announcements.add((String) announcement);
|
||||
}
|
||||
|
||||
serverGroup.setAnnouncements(announcements);
|
||||
serverGroup.save();
|
||||
APIv3.respondJson(ctx, serverGroup.getAnnouncements());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.frozenorb.apiv3.route.auditLog;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETAuditLog implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
try {
|
||||
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip"));
|
||||
int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
|
||||
|
||||
APIv3.respondJson(ctx, AuditLogEntry.findAllPaginatedSync(skip, pageSize));
|
||||
} catch (NumberFormatException ex) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package net.frozenorb.apiv3.route.auditLog;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLog;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import net.frozenorb.apiv3.util.IpUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
public final class POSTUserAuditLogEntry implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User user = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (user == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
String userIp = ctx.request().getParam("userIp");
|
||||
|
||||
if (!IpUtils.isValidIp(userIp)) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
AuditLogActionType type;
|
||||
|
||||
try {
|
||||
type = AuditLogActionType.valueOf(ctx.request().getParam("type"));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
ErrorUtils.respondNotFound(ctx, "Audit log action type", ctx.request().getParam("type"));
|
||||
return;
|
||||
}
|
||||
|
||||
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
|
||||
AuditLog.log(user, userIp, ctx.get("actor"), type, Document.parse(ctx.getBodyAsString()), blockingCallback);
|
||||
AuditLogEntry entry = blockingCallback.get();
|
||||
APIv3.respondJson(ctx, entry);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package net.frozenorb.apiv3.route.chatFilterList;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
|
||||
public final class GETChatFilterList implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
// TODO
|
||||
// WARNING: THIS IS REGISTERED ASYNC SO DONT MESS IT UP
|
||||
APIv3.respondJson(ctx, ImmutableList.of());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package net.frozenorb.apiv3.route.grants;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLog;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETEGrant implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
Grant grant = Grant.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (grant == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Grant", ctx.request().getParam("id"));
|
||||
return;
|
||||
} else if (!grant.isActive()) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Cannot remove an inactive grant.");
|
||||
return;
|
||||
}
|
||||
|
||||
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy"));
|
||||
|
||||
if (removedBy == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy"));
|
||||
return;
|
||||
}
|
||||
|
||||
String reason = ctx.request().getParam("reason");
|
||||
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "reason");
|
||||
return;
|
||||
}
|
||||
|
||||
grant.delete(removedBy, reason);
|
||||
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
|
||||
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of(), blockingCallback);
|
||||
blockingCallback.get();
|
||||
APIv3.respondJson(ctx, grant);
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/net/frozenorb/apiv3/route/grants/GETGrant.java
Normal file
14
src/main/java/net/frozenorb/apiv3/route/grants/GETGrant.java
Normal file
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.grants;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
|
||||
public final class GETGrant implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, Grant.findByIdSync(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.frozenorb.apiv3.route.grants;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETGrants implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
try {
|
||||
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip"));
|
||||
int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
|
||||
|
||||
APIv3.respondJson(ctx, Grant.findAllPaginatedSync(skip, pageSize));
|
||||
} catch (NumberFormatException ex) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.grants;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETUserGrants implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User target = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (target == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
APIv3.respondJson(ctx, Grant.findByUserSync(target));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package net.frozenorb.apiv3.route.grants;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
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.util.ErrorUtils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class POSTUserGrant implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User target = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (target == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
String reason = ctx.request().getParam("reason");
|
||||
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "reason");
|
||||
return;
|
||||
}
|
||||
|
||||
Set<ServerGroup> scopes = new HashSet<>();
|
||||
String scopesUnparsed = ctx.request().getParam("scopes");
|
||||
|
||||
if (!scopesUnparsed.isEmpty()) {
|
||||
for (String serverGroupId : scopesUnparsed.split(",")) {
|
||||
ServerGroup serverGroup = ServerGroup.findById(serverGroupId);
|
||||
|
||||
if (serverGroup == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server group", serverGroupId);
|
||||
return;
|
||||
}
|
||||
|
||||
scopes.add(serverGroup);
|
||||
}
|
||||
}
|
||||
|
||||
Rank rank = Rank.findById(ctx.request().getParam("rank"));
|
||||
|
||||
if (rank == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Rank", ctx.request().getParam("rank"));
|
||||
return;
|
||||
}
|
||||
|
||||
Date expiresAt;
|
||||
|
||||
try {
|
||||
expiresAt = new Date(Long.parseLong(ctx.request().getParam("expiresAt")));
|
||||
} catch (NumberFormatException ex) {
|
||||
expiresAt = null;
|
||||
}
|
||||
|
||||
if (expiresAt != null && expiresAt.before(new Date())) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past.");
|
||||
return;
|
||||
}
|
||||
|
||||
// We purposely don't do a null check, grants don't have to have a source.
|
||||
User addedBy = User.findByIdSync(ctx.request().getParam("addedBy"));
|
||||
|
||||
Grant grant = new Grant(target, reason, scopes, rank, expiresAt, addedBy);
|
||||
grant.insert();
|
||||
APIv3.respondJson(ctx, grant);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package net.frozenorb.apiv3.route.ipBans;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLog;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.model.IpBan;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETEIpBan implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
IpBan ipBan = IpBan.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (ipBan == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "IpBan", ctx.request().getParam("id"));
|
||||
return;
|
||||
} else if (!ipBan.isActive()) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Cannot remove an inactive ip ban.");
|
||||
return;
|
||||
}
|
||||
|
||||
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy"));
|
||||
|
||||
if (removedBy == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy"));
|
||||
return;
|
||||
}
|
||||
|
||||
String reason = ctx.request().getParam("reason");
|
||||
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "reason");
|
||||
return;
|
||||
}
|
||||
|
||||
ipBan.delete(removedBy, reason);
|
||||
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
|
||||
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback);
|
||||
blockingCallback.get();
|
||||
APIv3.respondJson(ctx, ipBan);
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBan.java
Normal file
14
src/main/java/net/frozenorb/apiv3/route/ipBans/GETIpBan.java
Normal file
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.ipBans;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.IpBan;
|
||||
|
||||
public final class GETIpBan implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, IpBan.findByIdSync(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.frozenorb.apiv3.route.ipBans;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.IpBan;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETIpBans implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
try {
|
||||
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip"));
|
||||
int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
|
||||
|
||||
APIv3.respondJson(ctx, IpBan.findAllPaginatedSync(skip, pageSize));
|
||||
} catch (NumberFormatException ex) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.ipBans;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.IpBan;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import net.frozenorb.apiv3.util.IpUtils;
|
||||
|
||||
public final class GETIpIpBans implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
String userIp = ctx.request().getParam("id");
|
||||
|
||||
if (!IpUtils.isValidIp(userIp)) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
APIv3.respondJson(ctx, IpBan.findByIpSync(userIp));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package net.frozenorb.apiv3.route.ipBans;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.IpBan;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import net.frozenorb.apiv3.util.IpUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public final class POSTIpIpBan implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
String userIp = ctx.request().getParam("id");
|
||||
|
||||
if (!IpUtils.isValidIp(userIp)) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
String reason = ctx.request().getParam("reason");
|
||||
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "reason");
|
||||
return;
|
||||
}
|
||||
|
||||
Date expiresAt;
|
||||
|
||||
try {
|
||||
expiresAt = new Date(Long.parseLong(ctx.request().getParam("expiresAt")));
|
||||
} catch (NumberFormatException ex) {
|
||||
expiresAt = null;
|
||||
}
|
||||
|
||||
if (expiresAt != null && expiresAt.before(new Date())) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past.");
|
||||
return;
|
||||
}
|
||||
|
||||
// We purposely don't do a null check, ip bans don't have to have a source.
|
||||
User addedBy = User.findByIdSync(ctx.request().getParam("addedBy"));
|
||||
|
||||
IpBan ipBan = new IpBan(userIp, reason, expiresAt, addedBy, ctx.get("actor"));
|
||||
ipBan.insert();
|
||||
|
||||
APIv3.respondJson(ctx, ipBan);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.ipLog;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.IpLogEntry;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETUserIpLog implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User target = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (target == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
APIv3.respondJson(ctx, IpLogEntry.findByUserSync(target));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.notificationTemplates;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.NotificationTemplate;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETENotificationTemplate implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
NotificationTemplate notificationTemplate = NotificationTemplate.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (notificationTemplate == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
notificationTemplate.delete();
|
||||
APIv3.respondJson(ctx, notificationTemplate);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.notificationTemplates;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.NotificationTemplate;
|
||||
|
||||
public final class GETNotificationTemplate implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, NotificationTemplate.findByIdSync(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.notificationTemplates;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.NotificationTemplate;
|
||||
|
||||
public final class GETNotificationTemplates implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, NotificationTemplate.findAllSync());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package net.frozenorb.apiv3.route.notificationTemplates;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.NotificationTemplate;
|
||||
|
||||
public final class POSTNotificationTemplate implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
String id = ctx.request().getParam("id");
|
||||
String subject = ctx.request().getParam("subject");
|
||||
String body = ctx.request().getParam("body");
|
||||
|
||||
NotificationTemplate notificationTemplate = new NotificationTemplate(id, subject, body);
|
||||
notificationTemplate.insert();
|
||||
APIv3.respondJson(ctx, notificationTemplate);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package net.frozenorb.apiv3.route.notificationTemplates;
|
||||
|
||||
public class PUTNotificationTemplate {
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package net.frozenorb.apiv3.route.punishments;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLog;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETEPunishment implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
Punishment punishment = Punishment.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (punishment == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Punishment", ctx.request().getParam("id"));
|
||||
return;
|
||||
} else if (!punishment.isActive()) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Cannot remove an inactive punishment.");
|
||||
return;
|
||||
}
|
||||
|
||||
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy"));
|
||||
|
||||
if (removedBy == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy"));
|
||||
return;
|
||||
}
|
||||
|
||||
String reason = ctx.request().getParam("reason");
|
||||
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "reason");
|
||||
return;
|
||||
}
|
||||
|
||||
punishment.delete(removedBy, reason);
|
||||
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
|
||||
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback);
|
||||
blockingCallback.get();
|
||||
APIv3.respondJson(ctx, punishment);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package net.frozenorb.apiv3.route.punishments;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLog;
|
||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||
import net.frozenorb.apiv3.model.AuditLogEntry;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETEUserPunishment implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User target = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (target == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type").toUpperCase());
|
||||
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy"));
|
||||
|
||||
if (removedBy == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy"));
|
||||
return;
|
||||
}
|
||||
|
||||
String reason = ctx.request().getParam("reason");
|
||||
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "reason");
|
||||
return;
|
||||
}
|
||||
|
||||
for (Punishment punishment : Punishment.findByUserAndTypeSync(target, ImmutableSet.of(type))) {
|
||||
if (punishment.isActive()) {
|
||||
punishment.delete(removedBy, reason);
|
||||
BlockingCallback<AuditLogEntry> blockingCallback = new BlockingCallback<>();
|
||||
AuditLog.log(removedBy, "", ctx.get("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of(), blockingCallback);
|
||||
blockingCallback.get();
|
||||
APIv3.respondJson(ctx, punishment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorUtils.respondGeneric(ctx, 404, "User provided has no active punishments");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.punishments;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
|
||||
public final class GETPunishment implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, Punishment.findByIdSync(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.frozenorb.apiv3.route.punishments;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETPunishments implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
try {
|
||||
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip"));
|
||||
int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
|
||||
|
||||
APIv3.respondJson(ctx, Punishment.findAllPaginatedSync(skip, pageSize));
|
||||
} catch (NumberFormatException ex) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.punishments;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETUserPunishments implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User target = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (target == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
APIv3.respondJson(ctx, Punishment.findByUserSync(target));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package net.frozenorb.apiv3.route.punishments;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.IpBan;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.Permissions;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
public final class POSTUserPunish implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User target = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (target == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
String reason = ctx.request().getParam("reason");
|
||||
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "reason");
|
||||
return;
|
||||
}
|
||||
|
||||
Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type"));
|
||||
|
||||
if (type != Punishment.PunishmentType.WARN) {
|
||||
for (Punishment punishment : Punishment.findByUserAndTypeSync(target, ImmutableSet.of(type))) {
|
||||
if (punishment.isActive()) {
|
||||
ErrorUtils.respondGeneric(ctx, 200, "A punishment by " + User.findByIdSync(punishment.getAddedBy()).getLastUsername() + " already covers this user.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Date expiresAt;
|
||||
|
||||
try {
|
||||
expiresAt = new Date(Long.parseLong(ctx.request().getParam("expiresAt")));
|
||||
} catch (NumberFormatException ex) {
|
||||
expiresAt = null;
|
||||
}
|
||||
|
||||
if (expiresAt != null && expiresAt.before(new Date())) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Expiration date cannot be in the past.");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> meta = Document.parse(ctx.getBodyAsString());
|
||||
|
||||
if (meta == null) {
|
||||
ErrorUtils.respondRequiredInput(ctx, "request body meta");
|
||||
return;
|
||||
}
|
||||
|
||||
// We purposely don't do a null check, punishments don't have to have a source.
|
||||
User addedBy = User.findByIdSync(ctx.request().getParam("addedBy"));
|
||||
|
||||
if (target.hasPermissionAnywhere(Permissions.PROTECTED_PUNISHMENT)) {
|
||||
ErrorUtils.respondGeneric(ctx, 200, target.getLastSeenOn() + " is protected from punishments.");
|
||||
return;
|
||||
}
|
||||
|
||||
Punishment punishment = new Punishment(target, reason, type, expiresAt, addedBy, ctx.get("actor"), meta);
|
||||
String accessDenialReason = punishment.getAccessDenialReason();
|
||||
String userIp = ctx.request().getParam("userIp");
|
||||
|
||||
if (userIp != null) {
|
||||
IpBan ipBan = new IpBan(userIp, punishment);
|
||||
ipBan.insert();
|
||||
|
||||
punishment.linkIpBan(ipBan);
|
||||
}
|
||||
|
||||
punishment.insert();
|
||||
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"punishment", punishment,
|
||||
"accessDenialReason", accessDenialReason == null ? "" : accessDenialReason
|
||||
));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.ranks;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Rank;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETERank implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
Rank rank = Rank.findById(ctx.request().getParam("id"));
|
||||
|
||||
if (rank == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Rank", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
rank.delete();
|
||||
APIv3.respondJson(ctx, rank);
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/net/frozenorb/apiv3/route/ranks/GETRank.java
Normal file
14
src/main/java/net/frozenorb/apiv3/route/ranks/GETRank.java
Normal file
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.ranks;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Rank;
|
||||
|
||||
public final class GETRank implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, Rank.findById(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/net/frozenorb/apiv3/route/ranks/GETRanks.java
Normal file
14
src/main/java/net/frozenorb/apiv3/route/ranks/GETRanks.java
Normal file
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.ranks;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Rank;
|
||||
|
||||
public final class GETRanks implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, Rank.findAll());
|
||||
}
|
||||
|
||||
}
|
23
src/main/java/net/frozenorb/apiv3/route/ranks/POSTRank.java
Normal file
23
src/main/java/net/frozenorb/apiv3/route/ranks/POSTRank.java
Normal file
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.ranks;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Rank;
|
||||
|
||||
public final class POSTRank implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
String id = ctx.request().getParam("id");
|
||||
int weight = Integer.parseInt(ctx.request().getParam("weight"));
|
||||
String displayName = ctx.request().getParam("displayName");
|
||||
String gameColor = ctx.request().getParam("gameColor");
|
||||
String websiteColor = ctx.request().getParam("websiteColor");
|
||||
boolean staffRank = Boolean.parseBoolean(ctx.request().getParam("staffRank"));
|
||||
|
||||
Rank rank = new Rank(id, weight, displayName, gameColor, websiteColor, staffRank);
|
||||
rank.insert();
|
||||
APIv3.respondJson(ctx, rank);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package net.frozenorb.apiv3.route.ranks;
|
||||
|
||||
public class PUTRank {
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.serverGroups;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETEServerGroup implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("id"));
|
||||
|
||||
if (serverGroup == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
serverGroup.delete();
|
||||
APIv3.respondJson(ctx, serverGroup);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.serverGroups;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
|
||||
public final class GETServerGroup implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, ServerGroup.findById(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.serverGroups;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
|
||||
public final class GETServerGroups implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, ServerGroup.findAll());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.frozenorb.apiv3.route.serverGroups;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
|
||||
public final class POSTServerGroup implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
String id = ctx.request().getParam("id");
|
||||
String image = ctx.request().getParam("image");
|
||||
|
||||
ServerGroup serverGroup = new ServerGroup(id, image);
|
||||
serverGroup.insert();
|
||||
APIv3.respondJson(ctx, serverGroup);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package net.frozenorb.apiv3.route.serverGroups;
|
||||
|
||||
public class PUTServerGroup {
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.frozenorb.apiv3.route.servers;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Server;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class DELETEServer implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
Server server = Server.findById(ctx.request().getParam("id"));
|
||||
|
||||
if (server == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
server.delete();
|
||||
APIv3.respondJson(ctx, server);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.servers;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Server;
|
||||
|
||||
public final class GETServer implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, Server.findById(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.servers;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Server;
|
||||
|
||||
public final class GETServers implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, Server.findAll());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package net.frozenorb.apiv3.route.servers;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Server;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import net.frozenorb.apiv3.util.IpUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
public final class POSTServer implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
String id = ctx.request().getParam("id");
|
||||
String displayName = ctx.request().getParam("displayName");
|
||||
ServerGroup group = ServerGroup.findById(ctx.request().getParam("group"));
|
||||
String ip = ctx.request().getParam("ip");
|
||||
|
||||
if (group == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("group"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IpUtils.isValidIp(ip)) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + ip + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
String generatedApiKey = new BigInteger(130, new Random()).toString(32);
|
||||
Server server = new Server(id, displayName, generatedApiKey, group, ip);
|
||||
server.insert();
|
||||
APIv3.respondJson(ctx, server);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
package net.frozenorb.apiv3.route.servers;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.CompositeFuture;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actor.Actor;
|
||||
import net.frozenorb.apiv3.actor.ActorType;
|
||||
import net.frozenorb.apiv3.model.*;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import net.frozenorb.apiv3.util.PermissionUtils;
|
||||
import net.frozenorb.apiv3.util.UUIDUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
public final class POSTServerHeartbeat implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
Actor actor = ctx.get("actor");
|
||||
|
||||
if (actor.getType() != ActorType.SERVER) {
|
||||
ErrorUtils.respondGeneric(ctx, 400, "This action can only be performed when requested by a server.");
|
||||
return;
|
||||
}
|
||||
|
||||
Server actorServer = Server.findById(actor.getName());
|
||||
ServerGroup actorServerGroup = ServerGroup.findById(actorServer.getServerGroup());
|
||||
Document reqJson = Document.parse(ctx.getBodyAsString());
|
||||
Map<UUID, String> playerNames = extractPlayerNames(reqJson);
|
||||
|
||||
CompositeFuture.all(
|
||||
createInfoResponse(actorServer, reqJson.getDouble("lastTps"), playerNames),
|
||||
createPlayerResponse(actorServer, playerNames),
|
||||
createPermissionsResponse(actorServerGroup),
|
||||
createEventsResponse((List<Object>) reqJson.get("events"))
|
||||
).setHandler((result) -> {
|
||||
if (result.succeeded()) {
|
||||
// We don't do anything with the info callback, as
|
||||
// it's just to update our database.
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"players", result.result().result(1),
|
||||
"permissions", result.result().result(2),
|
||||
"events", result.result().result(3)
|
||||
));
|
||||
} else {
|
||||
ErrorUtils.respondInternalError(ctx, result.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: ASYNC (MAKE ALL SAVES/INSERTS/ETC USED HERE ASYNC
|
||||
public Future<Void> createInfoResponse(Server server, double tps, Map<UUID, String> playerNames) {
|
||||
Future<Void> callback = Future.future();
|
||||
|
||||
server.setPlayers(playerNames.keySet());
|
||||
server.setLastTps(tps);
|
||||
server.setLastUpdatedAt(new Date());
|
||||
server.save();
|
||||
|
||||
callback.complete();
|
||||
return callback;
|
||||
}
|
||||
|
||||
public Future<Map<String, Object>> createPlayerResponse(Server server, Map<UUID, String> playerNames) {
|
||||
Future<Map<String, Object>> callback = Future.future();
|
||||
|
||||
Future<Map<UUID, User>> userLookupCallback = Future.future();
|
||||
Future<Map<UUID, List<Grant>>> grantLookupCallback = Future.future();
|
||||
Future<Map<UUID, List<Punishment>>> punishmentLookupCallback = Future.future();
|
||||
|
||||
User.findByIdGrouped(playerNames.keySet(), (users, error) -> {
|
||||
if (error != null) {
|
||||
userLookupCallback.fail(error);
|
||||
} else {
|
||||
userLookupCallback.complete(users);
|
||||
}
|
||||
});
|
||||
|
||||
Grant.findByUserGrouped(playerNames.keySet(), (grants, error) -> {
|
||||
if (error != null) {
|
||||
grantLookupCallback.fail(error);
|
||||
} else {
|
||||
grantLookupCallback.complete(grants);
|
||||
}
|
||||
});
|
||||
|
||||
Punishment.findByUserGrouped(playerNames.keySet(), (punishments, error) -> {
|
||||
if (error != null) {
|
||||
punishmentLookupCallback.fail(error);
|
||||
} else {
|
||||
punishmentLookupCallback.complete(punishments);
|
||||
}
|
||||
});
|
||||
|
||||
CompositeFuture.all(
|
||||
userLookupCallback,
|
||||
grantLookupCallback,
|
||||
punishmentLookupCallback
|
||||
).setHandler((result) -> {
|
||||
if (result.failed()) {
|
||||
callback.fail(result.cause());
|
||||
} else {
|
||||
Map<UUID, User> users = result.result().result(0);
|
||||
Map<UUID, List<Grant>> grants = result.result().result(1);
|
||||
Map<UUID, List<Punishment>> punishments = result.result().result(2);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
for (Map.Entry<UUID, User> userEntry : users.entrySet()) {
|
||||
UUID uuid = userEntry.getKey();
|
||||
User user = userEntry.getValue();
|
||||
|
||||
if (user == null) {
|
||||
String username = playerNames.get(uuid);
|
||||
user = new User(uuid, username);
|
||||
user.insert();
|
||||
users.put(uuid, user);
|
||||
}
|
||||
|
||||
// Only save if needed
|
||||
if (user.seenOnServer(server)) {
|
||||
user.save();
|
||||
}
|
||||
|
||||
// TODO: Provide IPs for ip ban lookup
|
||||
response.put(uuid.toString(), user.createLoginInfo(server, punishments.get(uuid), ImmutableList.of(), grants.get(uuid)));
|
||||
}
|
||||
|
||||
callback.complete(response);
|
||||
}
|
||||
});
|
||||
|
||||
return callback;
|
||||
}
|
||||
|
||||
public Future<Map<String, Object>> createPermissionsResponse(ServerGroup serverGroup) {
|
||||
Future<Map<String, Object>> callback = Future.future();
|
||||
Map<String, Object> permissionsResponse = new HashMap<>();
|
||||
|
||||
for (Rank rank : Rank.findAll()) {
|
||||
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
|
||||
PermissionUtils.getDefaultPermissions(rank),
|
||||
serverGroup.calculatePermissions(rank)
|
||||
);
|
||||
|
||||
permissionsResponse.put(rank.getId(), scopedPermissions);
|
||||
}
|
||||
|
||||
callback.complete(permissionsResponse);
|
||||
return callback;
|
||||
}
|
||||
|
||||
public Future<Map<String, Object>> createEventsResponse(List<Object> eventsData) {
|
||||
Future<Map<String, Object>> callback = Future.future();
|
||||
|
||||
for (Object event : eventsData) {
|
||||
Document eventJson = (Document) event;
|
||||
String type = eventJson.getString("type");
|
||||
|
||||
switch (type) {
|
||||
case "join":
|
||||
break;
|
||||
case "leave":
|
||||
break;
|
||||
case "metrics":
|
||||
break;
|
||||
default:
|
||||
log.warn("Recieved event with unknown type " + type + ".");
|
||||
}
|
||||
}
|
||||
|
||||
callback.complete(ImmutableMap.of());
|
||||
return callback;
|
||||
}
|
||||
|
||||
public Map<UUID, String> extractPlayerNames(Document reqJson) {
|
||||
Map<UUID, String> result = new HashMap<>();
|
||||
|
||||
for (Object player : (List<Object>) reqJson.get("players")) {
|
||||
Document playerJson = (Document) player;
|
||||
UUID uuid = UUID.fromString(playerJson.getString("uuid"));
|
||||
String username = playerJson.getString("username");
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(uuid)) {
|
||||
result.put(uuid, username);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package net.frozenorb.apiv3.route.servers;
|
||||
|
||||
public class PUTServer {
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.model.UserMetaEntry;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import org.bson.Document;
|
||||
|
||||
public final class DELETEUserMeta implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User user = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (user == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("serverGroup"));
|
||||
|
||||
if (serverGroup == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("serverGroup"));
|
||||
return;
|
||||
}
|
||||
|
||||
UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup);
|
||||
|
||||
if (userMetaEntry != null) {
|
||||
userMetaEntry.delete();
|
||||
APIv3.respondJson(ctx, userMetaEntry.getData());
|
||||
} else {
|
||||
APIv3.respondJson(ctx, new Document());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +1,35 @@
|
||||
package net.frozenorb.apiv3.routes.users;
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.models.Grant;
|
||||
import net.frozenorb.apiv3.models.Rank;
|
||||
import net.frozenorb.apiv3.models.User;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
import net.frozenorb.apiv3.model.Rank;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class GETStaff implements Route {
|
||||
public final class GETStaff implements Handler<RoutingContext> {
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
public void handle(RoutingContext ctx) {
|
||||
Map<String, Rank> staffRanks = new HashMap<>();
|
||||
|
||||
Rank.values().forEach(rank -> {
|
||||
Rank.findAll().forEach(rank -> {
|
||||
if (rank.isStaffRank()) {
|
||||
staffRanks.put(rank.getId(), rank);
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, Set<User>> result = new TreeMap<>((first, second) -> {
|
||||
Rank firstRank = Rank.byId(first);
|
||||
Rank secondRank = Rank.byId(second);
|
||||
Rank firstRank = staffRanks.get(first);
|
||||
Rank secondRank = staffRanks.get(second);
|
||||
|
||||
return Integer.compare(firstRank.getWeight(), secondRank.getWeight());
|
||||
});
|
||||
|
||||
APIv3.getDatastore().createQuery(Grant.class).field("rank").in(staffRanks.keySet()).forEach(grant -> {
|
||||
Grant.findByRankSync(staffRanks.values()).forEach(grant -> {
|
||||
if (grant.isActive()) {
|
||||
User user = User.byId(grant.getUser());
|
||||
User user = User.findByIdSync(grant.getUser());
|
||||
Rank rank = staffRanks.get(grant.getRank());
|
||||
|
||||
if (!result.containsKey(rank.getId())) {
|
||||
@ -41,7 +40,7 @@ public final class GETStaff implements Route {
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
APIv3.respondJson(ctx, result);
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/net/frozenorb/apiv3/route/users/GETUser.java
Normal file
14
src/main/java/net/frozenorb/apiv3/route/users/GETUser.java
Normal file
@ -0,0 +1,14 @@
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
|
||||
public final class GETUser implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
APIv3.respondJson(ctx, User.findByIdSync(ctx.request().getParam("id")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.Grant;
|
||||
import net.frozenorb.apiv3.model.IpLogEntry;
|
||||
import net.frozenorb.apiv3.model.Punishment;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETUserDetails implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User user = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (user == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Too many fields to use .of()
|
||||
APIv3.respondJson(ctx, ImmutableMap.builder()
|
||||
.put("user", user)
|
||||
.put("grants", Grant.findByUserSync(user))
|
||||
.put("ipLog", IpLogEntry.findByUserSync(user))
|
||||
.put("punishments", Punishment.findByUserSync(user))
|
||||
.put("aliases", user.getAliases())
|
||||
.put("totpSetup", user.getTotpSecret() != null)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.ServerGroup;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.model.UserMetaEntry;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETUserMeta implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User user = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (user == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
ServerGroup serverGroup = ServerGroup.findById(ctx.request().getParam("serverGroup"));
|
||||
|
||||
if (serverGroup == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("serverGroup"));
|
||||
return;
|
||||
}
|
||||
|
||||
UserMetaEntry userMetaEntry = UserMetaEntry.findByUserAndGroupSync(user, serverGroup);
|
||||
APIv3.respondJson(ctx, userMetaEntry.getData());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
import net.frozenorb.apiv3.util.IpUtils;
|
||||
import net.frozenorb.apiv3.util.TOTPUtils;
|
||||
|
||||
public final class GETUserRequiresTOTP implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User user = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (user == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.getTotpSecret() == null) {
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"required", false,
|
||||
"message", "User does not have TOTP setup."
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
String userIp = ctx.request().getParam("userIp");
|
||||
|
||||
if (!IpUtils.isValidIp(userIp)) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockingCallback<Boolean> preAuthorizedCallback = new BlockingCallback<>();
|
||||
TOTPUtils.isPreAuthorized(user, userIp, preAuthorizedCallback);
|
||||
|
||||
if (preAuthorizedCallback.get()) {
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"required", false,
|
||||
"message", "User's ip has already been validated"
|
||||
));
|
||||
} else {
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"required", true,
|
||||
"message", "User has no TOTP exemptions."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
public final class GETUserVerifyPassword implements Handler<RoutingContext> {
|
||||
|
||||
public void handle(RoutingContext ctx) {
|
||||
User user = User.findByIdSync(ctx.request().getParam("id"));
|
||||
|
||||
if (user == null) {
|
||||
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.getPassword() == null) {
|
||||
ErrorUtils.respondInvalidInput(ctx, "User provided does not have password set.");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean authorized = user.checkPassword(ctx.request().getParam("password"));
|
||||
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"authorized", authorized
|
||||
));
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +1,17 @@
|
||||
package net.frozenorb.apiv3.routes.users;
|
||||
package net.frozenorb.apiv3.route.users;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.models.User;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
import net.frozenorb.apiv3.model.User;
|
||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class POSTUserConfirmRegister implements Route {
|
||||
public final class POSTUserConfirmRegister implements Handler<RoutingContext> {
|
||||
|
||||
private final List<String> commonPasswords = ImmutableList.copyOf(("123456 password 12345678 qwerty 123456789 12345 1234 111111 1234567 dragon " +
|
||||
"123123 baseball abc123 football monkey letmein 696969 shadow master 666666 qwertyuiop 123321 mustang 1234567890 " +
|
||||
@ -22,38 +21,43 @@ public final class POSTUserConfirmRegister implements Route {
|
||||
" 131313 freedom 777777 pass fuck maggie 159753 aaaaaa ginger princess joshua cheese amanda summer love ashley 6969 " +
|
||||
"nicole chelsea biteme matthew access yankees 987654321 dallas austin thunder taylor matrix").split(" "));
|
||||
|
||||
public Object handle(Request req, Response res) {
|
||||
User user = User.byEmailToken(req.params("emailToken"));
|
||||
public void handle(RoutingContext ctx) {
|
||||
User user = User.findByEmailTokenSync(ctx.request().getParam("emailToken"));
|
||||
|
||||
if (user == null) {
|
||||
return ErrorUtils.notFound("Email token", req.params("emailToken"));
|
||||
ErrorUtils.respondNotFound(ctx, "Email token", ctx.request().getParam("emailToken"));
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't check email != null as that's set while we're pending
|
||||
// confirmation, we have to check the token.
|
||||
if (user.getEmailToken() == null) {
|
||||
return ErrorUtils.error("User provided already has email set.");
|
||||
ErrorUtils.respondGeneric(ctx, 400, "User provided already has email set.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((System.currentTimeMillis() - user.getEmailTokenSetAt().getTime()) > TimeUnit.DAYS.toMillis(2)) {
|
||||
return ErrorUtils.error("Email token is expired");
|
||||
ErrorUtils.respondGeneric(ctx, 200, "Email token is expired");
|
||||
return;
|
||||
}
|
||||
|
||||
String password = req.queryParams("password");
|
||||
String password = ctx.request().getParam("password");
|
||||
|
||||
if (password.length() < 8) {
|
||||
return ErrorUtils.error("Your password is too short.");
|
||||
ErrorUtils.respondGeneric(ctx, 200, "Your password is too short.");
|
||||
return;
|
||||
} else if (commonPasswords.contains(password)) {
|
||||
return ErrorUtils.error("Your password is too common. Please use a more secure password.");
|
||||
ErrorUtils.respondGeneric(ctx, 200, "Your password is too common. Please use a more secure password.");
|
||||
return;
|
||||
}
|
||||
|
||||
user.setEmailToken(null);
|
||||
user.setPassword(password);
|
||||
APIv3.getDatastore().save(user);
|
||||
user.save();
|
||||
|
||||
return ImmutableMap.of(
|
||||
APIv3.respondJson(ctx, ImmutableMap.of(
|
||||
"success", true
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user