From ddd0e0159a4b51fce0c10c4c1d9e3643050afb87 Mon Sep 17 00:00:00 2001 From: Colin McDonald Date: Thu, 23 Jun 2016 13:31:44 -0400 Subject: [PATCH] Integrate user logins with MaxMind, and deny access to users on VPNs --- .../java/net/frozenorb/apiv3/model/User.java | 49 ++++++++++++++++--- .../route/servers/POSTServersHeartbeat.java | 4 +- .../frozenorb/apiv3/unsorted/Permissions.java | 1 + 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/frozenorb/apiv3/model/User.java b/src/main/java/net/frozenorb/apiv3/model/User.java index b6d5954..541b121 100644 --- a/src/main/java/net/frozenorb/apiv3/model/User.java +++ b/src/main/java/net/frozenorb/apiv3/model/User.java @@ -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 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 grants) { Rank highest = null; @@ -289,7 +292,6 @@ public final class User { } } - // TODO: Clean public Map getHighestRanks() { Map highestRanks = new HashMap<>(); Rank defaultRank = Rank.findById("default"); @@ -318,6 +320,7 @@ public final class User { public void getLoginInfo(Server server, String userIp, SingleResultCallback> callback) { Future> punishmentsFuture = Future.future(); + Future ipIntelFuture = Future.future(); Future> ipBansFuture = Future.future(); Future> 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 punishments = result.result().result(0); - Iterable ipBans = result.result().result(1); - Iterable grants = result.result().result(2); + IpIntel ipIntel = result.result().result(1); + Iterable ipBans = result.result().result(2); + Iterable 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 createLoginInfo(Server server, Iterable punishments, Iterable ipBans, Iterable grants) { + public Map createLoginInfo(Server server, IpIntel ipIntel, Iterable punishments, Iterable ipBans, Iterable 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 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. diff --git a/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java b/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java index 159a8ef..46e3618 100644 --- a/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java +++ b/src/main/java/net/frozenorb/apiv3/route/servers/POSTServersHeartbeat.java @@ -130,8 +130,8 @@ public final class POSTServersHeartbeat implements Handler { 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); diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java b/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java index 5f711c9..b93774b 100644 --- a/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java +++ b/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java @@ -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"; } \ No newline at end of file