Integrate user logins with MaxMind, and deny access to users on VPNs
This commit is contained in:
parent
4e00381e50
commit
ddd0e0159a
|
@ -3,6 +3,7 @@ package net.frozenorb.apiv3.model;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
|
@ -17,10 +18,13 @@ import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.frozenorb.apiv3.APIv3;
|
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.gson.ExcludeFromReplies;
|
||||||
import net.frozenorb.apiv3.serialization.jackson.UuidJsonDeserializer;
|
import net.frozenorb.apiv3.serialization.jackson.UuidJsonDeserializer;
|
||||||
import net.frozenorb.apiv3.serialization.jackson.UuidJsonSerializer;
|
import net.frozenorb.apiv3.serialization.jackson.UuidJsonSerializer;
|
||||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||||
|
import net.frozenorb.apiv3.unsorted.Permissions;
|
||||||
import net.frozenorb.apiv3.util.*;
|
import net.frozenorb.apiv3.util.*;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
|
|
||||||
|
@ -187,13 +191,13 @@ public final class User {
|
||||||
if (!newUsername.equals(lastUsername)) {
|
if (!newUsername.equals(lastUsername)) {
|
||||||
this.lastUsername = newUsername;
|
this.lastUsername = newUsername;
|
||||||
|
|
||||||
// TODO: FIX MOJANG API CALL
|
|
||||||
User withNewUsername;
|
User withNewUsername;
|
||||||
|
|
||||||
while ((withNewUsername = User.findByLastUsernameSync(newUsername)) != null) {
|
while ((withNewUsername = User.findByLastUsernameSync(newUsername)) != null) {
|
||||||
BlockingCallback<String> callback = new BlockingCallback<>();
|
BlockingCallback<String> callback = new BlockingCallback<>();
|
||||||
MojangUtils.getName(withNewUsername.getId(), callback);
|
MojangUtils.getName(withNewUsername.getId(), callback);
|
||||||
withNewUsername.updateUsername(callback.get());
|
withNewUsername.updateUsername(callback.get());
|
||||||
|
withNewUsername.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +269,6 @@ public final class User {
|
||||||
return getHighestRankScoped(null, Grant.findByUserSync(this));
|
return getHighestRankScoped(null, Grant.findByUserSync(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Clean
|
|
||||||
// This is only used to help batch requests to mongo
|
// This is only used to help batch requests to mongo
|
||||||
public Rank getHighestRankScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
|
public Rank getHighestRankScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
|
||||||
Rank highest = null;
|
Rank highest = null;
|
||||||
|
@ -289,7 +292,6 @@ public final class User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Clean
|
|
||||||
public Map<ServerGroup, Rank> getHighestRanks() {
|
public Map<ServerGroup, Rank> getHighestRanks() {
|
||||||
Map<ServerGroup, Rank> highestRanks = new HashMap<>();
|
Map<ServerGroup, Rank> highestRanks = new HashMap<>();
|
||||||
Rank defaultRank = Rank.findById("default");
|
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) {
|
public void getLoginInfo(Server server, String userIp, SingleResultCallback<Map<String, Object>> callback) {
|
||||||
Future<Iterable<Punishment>> punishmentsFuture = Future.future();
|
Future<Iterable<Punishment>> punishmentsFuture = Future.future();
|
||||||
|
Future<IpIntel> ipIntelFuture = Future.future();
|
||||||
Future<Iterable<IpBan>> ipBansFuture = Future.future();
|
Future<Iterable<IpBan>> ipBansFuture = Future.future();
|
||||||
Future<Iterable<Grant>> grantsFuture = Future.future();
|
Future<Iterable<Grant>> grantsFuture = Future.future();
|
||||||
|
|
||||||
|
@ -334,6 +337,14 @@ public final class User {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (userIp != null) {
|
if (userIp != null) {
|
||||||
|
IpIntel.findByIdOrInsert(userIp, (ipIntel, error) -> {
|
||||||
|
if (error != null) {
|
||||||
|
ipIntelFuture.fail(error);
|
||||||
|
} else {
|
||||||
|
ipIntelFuture.complete(ipIntel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
IpBan.findByIp(userIp, (ipBans, error) -> {
|
IpBan.findByIp(userIp, (ipBans, error) -> {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
ipBansFuture.fail(error);
|
ipBansFuture.fail(error);
|
||||||
|
@ -342,6 +353,7 @@ public final class User {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
ipIntelFuture.complete(null);
|
||||||
ipBansFuture.complete(ImmutableSet.of());
|
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()) {
|
if (result.succeeded()) {
|
||||||
Iterable<Punishment> punishments = result.result().result(0);
|
Iterable<Punishment> punishments = result.result().result(0);
|
||||||
Iterable<IpBan> ipBans = result.result().result(1);
|
IpIntel ipIntel = result.result().result(1);
|
||||||
Iterable<Grant> grants = result.result().result(2);
|
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 {
|
} else {
|
||||||
callback.onResult(null, result.cause());
|
callback.onResult(null, result.cause());
|
||||||
}
|
}
|
||||||
|
@ -367,7 +380,7 @@ public final class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is only used to help batch requests to mongo
|
// 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 activeMute = null;
|
||||||
Punishment activeBan = null;
|
Punishment activeBan = null;
|
||||||
IpBan activeIpBan = null;
|
IpBan activeIpBan = null;
|
||||||
|
@ -414,6 +427,26 @@ public final class User {
|
||||||
"message", reason,
|
"message", reason,
|
||||||
"activeIpBanId", activeIpBan.getId()
|
"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.
|
// Generics are weird, yes we have to do this.
|
||||||
|
|
|
@ -130,8 +130,8 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
|
||||||
user.save();
|
user.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Provide IPs for ip ban lookup
|
// TODO: Provide IPs for ip ban lookup (and ip intel)
|
||||||
response.put(uuid.toString(), user.createLoginInfo(server, punishments.get(uuid), ImmutableList.of(), grants.get(uuid)));
|
response.put(uuid.toString(), user.createLoginInfo(server,null, punishments.get(uuid), ImmutableList.of(), grants.get(uuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.complete(response);
|
callback.complete(response);
|
||||||
|
|
|
@ -7,5 +7,6 @@ public class Permissions {
|
||||||
|
|
||||||
public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected";
|
public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected";
|
||||||
public static final String SIGN_API_REQUEST = "apiv3.signRequest";
|
public static final String SIGN_API_REQUEST = "apiv3.signRequest";
|
||||||
|
public static final String BYPASS_VPN_CHECK = "minehq.vpn.bypass";
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue