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.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -14,6 +15,7 @@ import io.vertx.core.Vertx;
class Main { class Main {
@Autowired private APIv3 verticle; @Autowired private APIv3 verticle;
@Autowired private Environment environment;
public static void main(String[] args) { public static void main(String[] args) {
System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory"); 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.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;
import com.mongodb.ConnectionString; import com.mongodb.ConnectionString;
import com.mongodb.MongoCredential;
import com.mongodb.async.client.MongoClient; import com.mongodb.async.client.MongoClient;
import com.mongodb.async.client.MongoClientSettings; import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.async.client.MongoClients; import com.mongodb.async.client.MongoClients;
import com.mongodb.async.client.MongoDatabase; import com.mongodb.async.client.MongoDatabase;
import com.mongodb.client.model.IndexModel; import com.mongodb.client.model.IndexModel;
import com.mongodb.connection.ClusterSettings; 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.InstantJsonDeserializer;
import net.frozenorb.apiv3.serialization.jackson.InstantJsonSerializer; import net.frozenorb.apiv3.serialization.jackson.InstantJsonSerializer;
@ -32,7 +35,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.time.Instant; import java.time.Instant;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import fr.javatic.mongo.jacksonCodec.JacksonCodecProvider; import fr.javatic.mongo.jacksonCodec.JacksonCodecProvider;
@ -42,82 +44,68 @@ import fr.javatic.mongo.jacksonCodec.ObjectMapperFactory;
public class MongoConfig { public class MongoConfig {
@Bean @Bean
public MongoDatabase mongoDatabase( public MongoDatabase mongoDatabase(@Value("${mongoUri}") String mongoUri) {
@Value("${mongo.username}") String username, ConnectionString connStr = new ConnectionString(mongoUri);
@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();
if (!username.isEmpty()) { // all of these lines except for .codecRegistry are copied from MongoClients#create(ConnectionString)
credentials = ImmutableList.of( MongoClient mongoClient = MongoClients.create(MongoClientSettings.builder()
MongoCredential.createCredential(username, database, password.toCharArray()) .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())
ConnectionString connectionString = new ConnectionString("mongodb://" + address + ":" + port); .socketSettings(SocketSettings.builder().applyConnectionString(connStr).build())
.codecRegistry(CodecRegistries.fromProviders(ImmutableList.of(
MongoClient mongoClient = MongoClients.create(MongoClientSettings new UuidCodecProvider(), // MHQ, fixes uuid serialization
.builder() new ValueCodecProvider(),
.codecRegistry(CodecRegistries.fromProviders(ImmutableList.of( new DocumentCodecProvider(),
new UuidCodecProvider(), // MHQ, fixes uuid serialization new BsonValueCodecProvider(),
new ValueCodecProvider(), new JacksonCodecProvider(createMongoJacksonMapper()) // Jackson codec, provides serialization/deserialization
new DocumentCodecProvider(), )))
new BsonValueCodecProvider(), .build()
new JacksonCodecProvider(createMongoJacksonMapper()) // Jackson codec, provides serialization/deserialization
)))
.credentialList(credentials)
.clusterSettings(ClusterSettings.builder()
.applyConnectionString(connectionString)
.build()
)
.build()
); );
MongoDatabase db = mongoClient.getDatabase(database); MongoDatabase database = mongoClient.getDatabase(connStr.getDatabase());
db.getCollection("auditLog").createIndexes(ImmutableList.of( database.getCollection("auditLog").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)), new IndexModel(new Document("user", 1)),
new IndexModel(new Document("performedAt", 1)), new IndexModel(new Document("performedAt", 1)),
new IndexModel(new Document("type", 1)) new IndexModel(new Document("type", 1))
), (a, b) -> {}); ), (a, b) -> {});
db.getCollection("grants").createIndexes(ImmutableList.of( database.getCollection("grants").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)), new IndexModel(new Document("user", 1)),
new IndexModel(new Document("rank", 1)), new IndexModel(new Document("rank", 1)),
new IndexModel(new Document("addedAt", 1)) new IndexModel(new Document("addedAt", 1))
), (a, b) -> {}); ), (a, b) -> {});
db.getCollection("ipLog").createIndexes(ImmutableList.of( database.getCollection("ipLog").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)), new IndexModel(new Document("user", 1)),
new IndexModel(new Document("user", 1).append("userIp", 1)), new IndexModel(new Document("user", 1).append("userIp", 1)),
new IndexModel(new Document("hashedUserIp", 1)) new IndexModel(new Document("hashedUserIp", 1))
), (a, b) -> {}); ), (a, b) -> {});
db.getCollection("ipBans").createIndexes(ImmutableList.of( database.getCollection("ipBans").createIndexes(ImmutableList.of(
new IndexModel(new Document("userIp", 1)) new IndexModel(new Document("userIp", 1))
), (a, b) -> {}); ), (a, b) -> {});
db.getCollection("ipIntel").createIndexes(ImmutableList.of( database.getCollection("ipIntel").createIndexes(ImmutableList.of(
new IndexModel(new Document("hashedIp", 1)), new IndexModel(new Document("hashedIp", 1)),
new IndexModel(new Document("location", "2dsphere")) new IndexModel(new Document("location", "2dsphere"))
), (a, b) -> {}); ), (a, b) -> {});
db.getCollection("punishments").createIndexes(ImmutableList.of( database.getCollection("punishments").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1)), new IndexModel(new Document("user", 1)),
new IndexModel(new Document("type", 1)), new IndexModel(new Document("type", 1)),
new IndexModel(new Document("addedAt", 1)), new IndexModel(new Document("addedAt", 1)),
new IndexModel(new Document("addedBy", 1)), new IndexModel(new Document("addedBy", 1)),
new IndexModel(new Document("linkedIpBanId", 1)) new IndexModel(new Document("linkedIpBanId", 1))
), (a, b) -> {}); ), (a, b) -> {});
db.getCollection("users").createIndexes(ImmutableList.of( database.getCollection("users").createIndexes(ImmutableList.of(
new IndexModel(new Document("lastUsername", 1)), new IndexModel(new Document("lastUsername", 1)),
new IndexModel(new Document("lastUsernameLower", 1)), new IndexModel(new Document("lastUsernameLower", 1)),
new IndexModel(new Document("emailToken", 1)), new IndexModel(new Document("emailToken", 1)),
new IndexModel(new Document("totpSecret", 1)) new IndexModel(new Document("totpSecret", 1))
), (a, b) -> {}); ), (a, b) -> {});
db.getCollection("userMeta").createIndexes(ImmutableList.of( database.getCollection("userMeta").createIndexes(ImmutableList.of(
new IndexModel(new Document("user", 1).append("serverGroup", 1)) new IndexModel(new Document("user", 1).append("serverGroup", 1))
), (a, b) -> {}); ), (a, b) -> {});
return db; return database;
} }
private ObjectMapper createMongoJacksonMapper() { 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.net.URI;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.redis.RedisClient; import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions; import io.vertx.redis.RedisOptions;
@ -17,11 +19,10 @@ public class RedisConfig {
} }
@Bean @Bean
public RedisOptions redisOptions( public RedisOptions redisOptions(@Value("${redisUri}") URI redisUri) {
@Value("${redis.address}") String address, return new RedisOptions()
@Value("${redis.port}") int port .setAddress(redisUri.getHost())
) { .setPort(redisUri.getPort());
return new RedisOptions().setAddress(address).setPort(port);
} }
} }

View File

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

View File

@ -47,7 +47,6 @@ import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import fr.javatic.mongo.jacksonCodec.Entity; import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id; import fr.javatic.mongo.jacksonCodec.objectId.Id;
@ -585,7 +584,7 @@ public final class User {
Future<Void> markPreAuthFuture = Future.future(); Future<Void> markPreAuthFuture = Future.future();
Future<Void> markRecentlyUsedFuture = 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)); totpService.markRecentlyUsed(this, code, new MongoToVertxCallback<>(markRecentlyUsedFuture));
CompositeFuture.all(markPreAuthFuture, markRecentlyUsedFuture).setHandler((result) -> { 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 net.frozenorb.apiv3.util.IpUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import io.vertx.redis.RedisClient; import io.vertx.redis.RedisClient;
@Component @Component
public final class RedisTotpService implements TotpService { public final class RedisTotpService implements TotpService {
private final GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build());
@Autowired private RedisClient redisClient; @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 @Override
public boolean authorizeUser(String secret, int code) { public boolean authorizeUser(String secret, int code) {
@ -42,7 +59,7 @@ public final class RedisTotpService implements TotpService {
} }
@Override @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)) { if (!IpUtils.isValidIp(ip)) {
callback.onResult(null, null); callback.onResult(null, null);
return; return;
@ -50,7 +67,7 @@ public final class RedisTotpService implements TotpService {
String key = user.getId() + ":preAuthorizedIp:" + ip.toLowerCase(); 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()) { if (result.succeeded()) {
callback.onResult(null, null); callback.onResult(null, null);
} else { } else {
@ -74,7 +91,7 @@ public final class RedisTotpService implements TotpService {
public void markRecentlyUsed(User user, int code, SingleResultCallback<Void> callback) { public void markRecentlyUsed(User user, int code, SingleResultCallback<Void> callback) {
String key = user.getId() + ":recentTotpCodes:" + code; String key = user.getId() + ":recentTotpCodes:" + code;
redisClient.setex(key, TimeUnit.MINUTES.toSeconds(5), "", (result) -> { redisClient.setex(key, recentlyUsedPeriodSeconds, "", (result) -> {
if (result.succeeded()) { if (result.succeeded()) {
callback.onResult(null, null); callback.onResult(null, null);
} else { } else {

View File

@ -6,8 +6,6 @@ import net.frozenorb.apiv3.model.User;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service @Service
public interface TotpService { public interface TotpService {
@ -15,7 +13,7 @@ public interface TotpService {
void isPreAuthorized(User user, String ip, SingleResultCallback<Boolean> callback); 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); 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 com.mongodb.async.SingleResultCallback;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
@ -15,6 +16,7 @@ import io.vertx.redis.RedisClient;
public final class RedisUserSessionService implements UserSessionService { public final class RedisUserSessionService implements UserSessionService {
@Autowired private RedisClient redisClient; @Autowired private RedisClient redisClient;
@Value("${userSession.sessionExpirationTimeDays}") private int sessionExpirationTimeDays;
@Override @Override
public void sessionExists(String userIp, String userSession, SingleResultCallback<Boolean> callback) { 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 userSession = UUID.randomUUID().toString().replaceAll("-", "");
String key = "apiv3:sessions:" + userIp + ":" + userSession; 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()) { if (result.succeeded()) {
redisClient.sadd("apiv3:sessions:" + user, key, (result2) -> { redisClient.sadd("apiv3:sessions:" + user, key, (result2) -> {
if (result2.succeeded()) { if (result2.succeeded()) {