Config changes + cleanup

This commit is contained in:
Colin McDonald 2016-11-29 22:10:02 -05:00
parent f4b59cf9b3
commit db0b173f15
10 changed files with 125 additions and 105 deletions

View File

@ -1,23 +0,0 @@
mongo.address=158.69.26.208
mongo.port=27027
mongo.database=MineHQDev
mongo.username=
mongo.password=
redis.address=209.222.96.50
redis.port=6379
http.port=80
http.keystoreFile=
http.keystorePassword=
mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ
mandrill.fromEmail=no-reply@minehq.com
mandrill.fromName=MineHQ Network
maxMind.userId=66817
maxMind.licenseKey=8Aw9NsOUeOp7
zang.accountSid=ACf18890845596403e330944d98886440c
zang.authToken=dc70bbd1fbd8411ba133fa93813a461b
zang.fromNumber=339-337-5300

35
application.yml Normal file
View File

@ -0,0 +1,35 @@
mongoUri: mongodb://158.69.26.208:27027/MineHQ
redisUri: redis://209.222.96.50:6379
http:
port: 80
keystoreFile:
keystorePassword:
disposableLoginToken:
tokenLifetimeSeconds: 300
ipHashing:
salt: J$gMsq6#!sWTK^JvB!px
userSession:
sessionExpirationTimeDays: 30
totp:
windowSize: 10
recentlyUsedPeriodSeconds: 300
ipAuthorizationDays: 5
maxMind:
userId: 66817
licenseKey: 8Aw9NsOUeOp7
mandrill:
apiKey: 0OYtwymqJP6oqvszeJu0vQ
fromEmail: no-reply@minehq.com
fromName: MineHQ Network
zang:
accountSid: ACf18890845596403e330944d98886440c
authToken: dc70bbd1fbd8411ba133fa93813a461b
fromNumber: 339-337-5300

View File

@ -3,6 +3,7 @@ package net.frozenorb.apiv3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.annotation.PostConstruct;
@ -14,6 +15,7 @@ import io.vertx.core.Vertx;
class Main {
@Autowired private APIv3 verticle;
@Autowired private Environment environment;
public static void main(String[] args) {
System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory");

View File

@ -8,13 +8,16 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.mongodb.ConnectionString;
import com.mongodb.MongoCredential;
import com.mongodb.async.client.MongoClient;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.async.client.MongoClients;
import com.mongodb.async.client.MongoDatabase;
import com.mongodb.client.model.IndexModel;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.connection.ServerSettings;
import com.mongodb.connection.SocketSettings;
import com.mongodb.connection.SslSettings;
import net.frozenorb.apiv3.serialization.jackson.InstantJsonDeserializer;
import net.frozenorb.apiv3.serialization.jackson.InstantJsonSerializer;
@ -32,7 +35,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import fr.javatic.mongo.jacksonCodec.JacksonCodecProvider;
@ -42,82 +44,68 @@ import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory;
public class MongoConfig {
@Bean
public MongoDatabase mongoDatabase(
@Value("${mongo.username}") String username,
@Value("${mongo.database}") String database,
@Value("${mongo.password}") String password,
@Value("${mongo.address}") String address,
@Value("${mongo.port}") int port
) {
List<MongoCredential> credentials = ImmutableList.of();
public MongoDatabase mongoDatabase(@Value("${mongoUri}") String mongoUri) {
ConnectionString connStr = new ConnectionString(mongoUri);
if (!username.isEmpty()) {
credentials = ImmutableList.of(
MongoCredential.createCredential(username, database, password.toCharArray())
);
}
ConnectionString connectionString = new ConnectionString("mongodb://" + address + ":" + port);
MongoClient mongoClient = MongoClients.create(MongoClientSettings
.builder()
.codecRegistry(CodecRegistries.fromProviders(ImmutableList.of(
new UuidCodecProvider(), // MHQ, fixes uuid serialization
new ValueCodecProvider(),
new DocumentCodecProvider(),
new BsonValueCodecProvider(),
new JacksonCodecProvider(createMongoJacksonMapper()) // Jackson codec, provides serialization/deserialization
)))
.credentialList(credentials)
.clusterSettings(ClusterSettings.builder()
.applyConnectionString(connectionString)
.build()
)
.build()
// all of these lines except for .codecRegistry are copied from MongoClients#create(ConnectionString)
MongoClient mongoClient = MongoClients.create(MongoClientSettings.builder()
.clusterSettings(ClusterSettings.builder().applyConnectionString(connStr).build())
.connectionPoolSettings(ConnectionPoolSettings.builder().applyConnectionString(connStr).build())
.serverSettings(ServerSettings.builder().build()).credentialList(connStr.getCredentialList())
.sslSettings(SslSettings.builder().applyConnectionString(connStr).build())
.socketSettings(SocketSettings.builder().applyConnectionString(connStr).build())
.codecRegistry(CodecRegistries.fromProviders(ImmutableList.of(
new UuidCodecProvider(), // MHQ, fixes uuid serialization
new ValueCodecProvider(),
new DocumentCodecProvider(),
new BsonValueCodecProvider(),
new JacksonCodecProvider(createMongoJacksonMapper()) // Jackson codec, provides serialization/deserialization
)))
.build()
);
MongoDatabase db = mongoClient.getDatabase(database);
MongoDatabase database = mongoClient.getDatabase(connStr.getDatabase());
db.getCollection("auditLog").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)),
new IndexModel(new Document("performedAt", 1)),
new IndexModel(new Document("type", 1))
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) -> {});
db.getCollection("grants").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)),
new IndexModel(new Document("rank", 1)),
new IndexModel(new Document("addedAt", 1))
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) -> {});
db.getCollection("ipLog").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)),
new IndexModel(new Document("user", 1).append("userIp", 1)),
new IndexModel(new Document("hashedUserIp", 1))
database.getCollection("ipLog").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)),
new IndexModel(new Document("user", 1).append("userIp", 1)),
new IndexModel(new Document("hashedUserIp", 1))
), (a, b) -> {});
db.getCollection("ipBans").createIndexes(ImmutableList.of(
new IndexModel(new Document("userIp", 1))
database.getCollection("ipBans").createIndexes(ImmutableList.of(
new IndexModel(new Document("userIp", 1))
), (a, b) -> {});
db.getCollection("ipIntel").createIndexes(ImmutableList.of(
new IndexModel(new Document("hashedIp", 1)),
new IndexModel(new Document("location", "2dsphere"))
database.getCollection("ipIntel").createIndexes(ImmutableList.of(
new IndexModel(new Document("hashedIp", 1)),
new IndexModel(new Document("location", "2dsphere"))
), (a, b) -> {});
db.getCollection("punishments").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)),
new IndexModel(new Document("type", 1)),
new IndexModel(new Document("addedAt", 1)),
new IndexModel(new Document("addedBy", 1)),
new IndexModel(new Document("linkedIpBanId", 1))
database.getCollection("punishments").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)),
new IndexModel(new Document("type", 1)),
new IndexModel(new Document("addedAt", 1)),
new IndexModel(new Document("addedBy", 1)),
new IndexModel(new Document("linkedIpBanId", 1))
), (a, b) -> {});
db.getCollection("users").createIndexes(ImmutableList.of(
new IndexModel(new Document("lastUsername", 1)),
new IndexModel(new Document("lastUsernameLower", 1)),
new IndexModel(new Document("emailToken", 1)),
new IndexModel(new Document("totpSecret", 1))
database.getCollection("users").createIndexes(ImmutableList.of(
new IndexModel(new Document("lastUsername", 1)),
new IndexModel(new Document("lastUsernameLower", 1)),
new IndexModel(new Document("emailToken", 1)),
new IndexModel(new Document("totpSecret", 1))
), (a, b) -> {});
db.getCollection("userMeta").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1).append("serverGroup", 1))
database.getCollection("userMeta").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1).append("serverGroup", 1))
), (a, b) -> {});
return db;
return database;
}
private ObjectMapper createMongoJacksonMapper() {

View File

@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.URI;
import io.vertx.core.Vertx;
import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions;
@ -17,11 +19,10 @@ public class RedisConfig {
}
@Bean
public RedisOptions redisOptions(
@Value("${redis.address}") String address,
@Value("${redis.port}") int port
) {
return new RedisOptions().setAddress(address).setPort(port);
public RedisOptions redisOptions(@Value("${redisUri}") URI redisUri) {
return new RedisOptions()
.setAddress(redisUri.getHost())
.setPort(redisUri.getPort());
}
}

View File

@ -5,10 +5,10 @@ import com.mongodb.async.SingleResultCallback;
import net.frozenorb.apiv3.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import io.vertx.redis.RedisClient;
@ -16,6 +16,7 @@ import io.vertx.redis.RedisClient;
public final class RedisDisposableLoginTokenService implements DisposableLoginTokenService {
@Autowired private RedisClient redisClient;
@Value("${disposableLoginToken.tokenLifetimeSeconds}") private int tokenLifetimeSeconds;
@Override
public void attemptLogin(String token, String userIp, SingleResultCallback<User> callback) {
@ -56,7 +57,7 @@ public final class RedisDisposableLoginTokenService implements DisposableLoginTo
public void createToken(UUID user, String userIp, SingleResultCallback<String> callback) {
String token = UUID.randomUUID().toString().replaceAll("-", "");
redisClient.setex("apiv3:disposableLoginTokens:" + userIp + ":" + token, TimeUnit.MINUTES.toSeconds(5), user.toString(), (result) -> {
redisClient.setex("apiv3:disposableLoginTokens:" + userIp + ":" + token, tokenLifetimeSeconds, user.toString(), (result) -> {
if (result.succeeded()) {
callback.onResult(token, null);
} else {

View File

@ -47,7 +47,6 @@ import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id;
@ -585,7 +584,7 @@ public final class User {
Future<Void> markPreAuthFuture = Future.future();
Future<Void> markRecentlyUsedFuture = Future.future();
totpService.markPreAuthorized(this, ip, 3, TimeUnit.DAYS, new MongoToVertxCallback<>(markPreAuthFuture));
totpService.markPreAuthorized(this, ip, new MongoToVertxCallback<>(markPreAuthFuture));
totpService.markRecentlyUsed(this, code, new MongoToVertxCallback<>(markRecentlyUsedFuture));
CompositeFuture.all(markPreAuthFuture, markRecentlyUsedFuture).setHandler((result) -> {

View File

@ -8,17 +8,34 @@ import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.util.IpUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import io.vertx.redis.RedisClient;
@Component
public final class RedisTotpService implements TotpService {
private final GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build());
@Autowired private RedisClient redisClient;
@Value("${totp.windowSize}") int windowSize;
@Value("${totp.recentlyUsedPeriodSeconds}") int recentlyUsedPeriodSeconds;
@Value("${totp.ipAuthorizationDays}") int ipAuthorizationDays;
private GoogleAuthenticator googleAuthenticator;
// has to be ran after construction (or else windowSize won't be defined when we go to
// create this object)
@PostConstruct
private void setupGoogleAuth() {
googleAuthenticator = new GoogleAuthenticator(
new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
.setWindowSize(windowSize)
.build()
);
}
@Override
public boolean authorizeUser(String secret, int code) {
@ -42,7 +59,7 @@ public final class RedisTotpService implements TotpService {
}
@Override
public void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback<Void> callback) {
public void markPreAuthorized(User user, String ip, SingleResultCallback<Void> callback) {
if (!IpUtils.isValidIp(ip)) {
callback.onResult(null, null);
return;
@ -50,7 +67,7 @@ public final class RedisTotpService implements TotpService {
String key = user.getId() + ":preAuthorizedIp:" + ip.toLowerCase();
redisClient.setex(key, unit.toSeconds(duration), "", (result) -> {
redisClient.setex(key, TimeUnit.DAYS.toSeconds(ipAuthorizationDays), "", (result) -> {
if (result.succeeded()) {
callback.onResult(null, null);
} else {
@ -74,7 +91,7 @@ public final class RedisTotpService implements TotpService {
public void markRecentlyUsed(User user, int code, SingleResultCallback<Void> callback) {
String key = user.getId() + ":recentTotpCodes:" + code;
redisClient.setex(key, TimeUnit.MINUTES.toSeconds(5), "", (result) -> {
redisClient.setex(key, recentlyUsedPeriodSeconds, "", (result) -> {
if (result.succeeded()) {
callback.onResult(null, null);
} else {

View File

@ -6,8 +6,6 @@ import net.frozenorb.apiv3.model.User;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public interface TotpService {
@ -15,7 +13,7 @@ public interface TotpService {
void isPreAuthorized(User user, String ip, SingleResultCallback<Boolean> callback);
void markPreAuthorized(User user, String ip, long duration, TimeUnit unit, SingleResultCallback<Void> callback);
void markPreAuthorized(User user, String ip, SingleResultCallback<Void> callback);
void wasRecentlyUsed(User user, int code, SingleResultCallback<Boolean> callback);

View File

@ -3,6 +3,7 @@ package net.frozenorb.apiv3.usersession;
import com.mongodb.async.SingleResultCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;
@ -15,6 +16,7 @@ import io.vertx.redis.RedisClient;
public final class RedisUserSessionService implements UserSessionService {
@Autowired private RedisClient redisClient;
@Value("${userSession.sessionExpirationTimeDays}") private int sessionExpirationTimeDays;
@Override
public void sessionExists(String userIp, String userSession, SingleResultCallback<Boolean> callback) {
@ -37,7 +39,7 @@ public final class RedisUserSessionService implements UserSessionService {
String userSession = UUID.randomUUID().toString().replaceAll("-", "");
String key = "apiv3:sessions:" + userIp + ":" + userSession;
redisClient.setex(key, TimeUnit.DAYS.toSeconds(30), "", (result) -> {
redisClient.setex(key, TimeUnit.DAYS.toSeconds(sessionExpirationTimeDays), "", (result) -> {
if (result.succeeded()) {
redisClient.sadd("apiv3:sessions:" + user, key, (result2) -> {
if (result2.succeeded()) {