diff --git a/src/main/java/de/nexuslobby/modules/security/SecurityModule.java b/src/main/java/de/nexuslobby/modules/security/SecurityModule.java new file mode 100644 index 0000000..96d7345 --- /dev/null +++ b/src/main/java/de/nexuslobby/modules/security/SecurityModule.java @@ -0,0 +1,119 @@ +package de.nexuslobby.modules.security; + +import de.nexuslobby.NexusLobby; +import de.nexuslobby.api.Module; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.UUID; + +public class SecurityModule implements Module, Listener { + + private final NexusLobby plugin = NexusLobby.getInstance(); + private LuckPerms luckPerms; + + @Override + public String getName() { return "Security"; } + + @Override + public void onEnable() { + if (Bukkit.getPluginManager().getPlugin("LuckPerms") != null) { + this.luckPerms = LuckPermsProvider.get(); + } + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onAsyncLogin(AsyncPlayerPreLoginEvent event) { + FileConfiguration config = plugin.getConfig(); + UUID uuid = event.getUniqueId(); + String ip = event.getAddress().getHostAddress(); + + // 1. Bypass Check (LuckPerms) + if (hasBypass(uuid)) { + return; // Teammitglieder dürfen immer drauf + } + + boolean vpnEnabled = config.getBoolean("security.vpn-blocker.enabled", true); + boolean countryEnabled = config.getBoolean("security.country-blocker.enabled", true); + + if (!vpnEnabled && !countryEnabled) return; + + // Lokale IPs überspringen + if (ip.equals("127.0.0.1") || ip.startsWith("192.168.") || ip.equals("0:0:0:0:0:0:0:1")) return; + + try { + URL url = new URL("http://ip-api.com/json/" + ip + "?fields=status,countryCode,proxy,hosting"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(3000); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + JSONObject json = (JSONObject) new JSONParser().parse(reader); + + if (json != null && "success".equals(json.get("status"))) { + + // 2. VPN / Proxy Check + if (vpnEnabled) { + boolean isProxy = (boolean) json.get("proxy"); + boolean isHosting = (boolean) json.get("hosting"); + if (isProxy || isHosting) { + event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, + colorize(config.getString("security.vpn-blocker.kick-message"))); + return; + } + } + + // 3. Country Check + if (countryEnabled) { + String countryCode = (String) json.get("countryCode"); + List allowed = config.getStringList("security.country-blocker.allowed-countries"); + + if (!allowed.isEmpty() && !allowed.contains(countryCode)) { + String kickMsg = config.getString("security.country-blocker.kick-message", "&cLand blockiert") + .replace("%country%", countryCode); + event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, colorize(kickMsg)); + } + } + } + } + } catch (Exception e) { + plugin.getLogger().warning("Security-Check fehlgeschlagen für " + ip + ": " + e.getMessage()); + } + } + + /** + * Prüft via LuckPerms, ob ein Spieler die Bypass-Berechtigung hat, + * noch bevor er vollständig auf dem Server ist. + */ + private boolean hasBypass(UUID uuid) { + if (luckPerms == null) return false; + User user = luckPerms.getUserManager().getUser(uuid); + if (user == null) { + // Falls User nicht geladen, versuchen wir ihn kurz zu laden (blockierend im AsyncEvent okay) + user = luckPerms.getUserManager().loadUser(uuid).join(); + } + return user != null && user.getCachedData().getPermissionData().checkPermission("nexuslobby.security.bypass").asBoolean(); + } + + private String colorize(String s) { + return s == null ? "" : s.replace("&", "§"); + } + + @Override + public void onDisable() {} +} \ No newline at end of file