Integrate user logins with MaxMind, and deny access to users on VPNs

This commit is contained in:
Colin McDonald 2016-06-23 13:31:44 -04:00
parent 4e00381e50
commit ddd0e0159a
3 changed files with 44 additions and 10 deletions

View File

@ -3,6 +3,7 @@ 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.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.Hashing;
@ -17,10 +18,13 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.maxmind.MaxMindResult;
import net.frozenorb.apiv3.maxmind.MaxMindUserType;
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.unsorted.Permissions;
import net.frozenorb.apiv3.util.*;
import org.bson.Document;
@ -187,13 +191,13 @@ public final class User {
if (!newUsername.equals(lastUsername)) {
this.lastUsername = newUsername;
// TODO: FIX MOJANG API CALL
User withNewUsername;
while ((withNewUsername = User.findByLastUsernameSync(newUsername)) != null) {
BlockingCallback<String> callback = new BlockingCallback<>();
MojangUtils.getName(withNewUsername.getId(), callback);
withNewUsername.updateUsername(callback.get());
withNewUsername.save();
}
}
@ -265,7 +269,6 @@ public final class User {
return getHighestRankScoped(null, 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;
@ -289,7 +292,6 @@ public final class User {
}
}
// TODO: Clean
public Map<ServerGroup, Rank> getHighestRanks() {
Map<ServerGroup, Rank> highestRanks = new HashMap<>();
Rank defaultRank = Rank.findById("default");
@ -318,6 +320,7 @@ public final class User {
public void getLoginInfo(Server server, String userIp, SingleResultCallback<Map<String, Object>> callback) {
Future<Iterable<Punishment>> punishmentsFuture = Future.future();
Future<IpIntel> ipIntelFuture = Future.future();
Future<Iterable<IpBan>> ipBansFuture = Future.future();
Future<Iterable<Grant>> grantsFuture = Future.future();
@ -334,6 +337,14 @@ public final class User {
});
if (userIp != null) {
IpIntel.findByIdOrInsert(userIp, (ipIntel, error) -> {
if (error != null) {
ipIntelFuture.fail(error);
} else {
ipIntelFuture.complete(ipIntel);
}
});
IpBan.findByIp(userIp, (ipBans, error) -> {
if (error != null) {
ipBansFuture.fail(error);
@ -342,6 +353,7 @@ public final class User {
}
});
} else {
ipIntelFuture.complete(null);
ipBansFuture.complete(ImmutableSet.of());
}
@ -353,13 +365,14 @@ public final class User {
}
});
CompositeFuture.all(punishmentsFuture, ipBansFuture, grantsFuture).setHandler((result) -> {
CompositeFuture.all(punishmentsFuture, ipIntelFuture, 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);
IpIntel ipIntel = result.result().result(1);
Iterable<IpBan> ipBans = result.result().result(2);
Iterable<Grant> grants = result.result().result(3);
callback.onResult(createLoginInfo(server, punishments, ipBans, grants), null);
callback.onResult(createLoginInfo(server, ipIntel, punishments, ipBans, grants), null);
} else {
callback.onResult(null, result.cause());
}
@ -367,7 +380,7 @@ public final class User {
}
// 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) {
public Map<String, Object> createLoginInfo(Server server, IpIntel ipIntel, Iterable<Punishment> punishments, Iterable<IpBan> ipBans, Iterable<Grant> grants) {
Punishment activeMute = null;
Punishment activeBan = null;
IpBan activeIpBan = null;
@ -414,6 +427,26 @@ public final class User {
"message", reason,
"activeIpBanId", activeIpBan.getId()
);
} else if (ipIntel != null) {
MaxMindResult maxMindResult = ipIntel.getResult();
MaxMindUserType userType = maxMindResult.getTraits().getUserType();
Map<String, Object> proposedAccess = null;
if (!userType.isAllowed()) {
proposedAccess = ImmutableMap.of(
"allowed", false,
"message", "You cannot join MineHQ from a VPN.",
"userType", userType.name()
);
} else if (ImmutableList.of().contains(maxMindResult.getTraits().getAsn())) {
// TODO: BANNED ASNS
}
// We do this to avoid making an expensive .hasPermissionAnywhere call unless we need to.
// TODO: THIS IS BLOCKING :(
if (proposedAccess != null && !hasPermissionAnywhere(Permissions.BYPASS_VPN_CHECK)) {
access = proposedAccess;
}
}
// Generics are weird, yes we have to do this.

View File

@ -130,8 +130,8 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
user.save();
}
// TODO: Provide IPs for ip ban lookup
response.put(uuid.toString(), user.createLoginInfo(server, punishments.get(uuid), ImmutableList.of(), grants.get(uuid)));
// TODO: Provide IPs for ip ban lookup (and ip intel)
response.put(uuid.toString(), user.createLoginInfo(server,null, punishments.get(uuid), ImmutableList.of(), grants.get(uuid)));
}
callback.complete(response);

View File

@ -7,5 +7,6 @@ public class Permissions {
public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected";
public static final String SIGN_API_REQUEST = "apiv3.signRequest";
public static final String BYPASS_VPN_CHECK = "minehq.vpn.bypass";
}