From 40425ac4e19519d1ddfeb8897edd3bb3ad254632 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 3 Oct 2024 18:48:19 +0200 Subject: [PATCH] Ban ip, macs & hwid in PG Finally rid of all db code in Client --- src/main/java/client/Client.java | 61 ------------------- src/main/java/database/account/Account.java | 4 +- .../database/account/AccountRepository.java | 30 ++++----- .../database/account/AccountRowMapper.java | 6 ++ .../java/database/ban/HwidBanRepository.java | 17 ++++++ src/main/java/server/ban/HwidBanManager.java | 7 +++ src/main/java/service/AccountService.java | 4 +- src/main/java/service/BanService.java | 47 ++++++++++++-- src/main/java/service/TransitionService.java | 2 +- .../db/migration/postgresql/V0.10__ban.sql | 2 +- 10 files changed, 94 insertions(+), 86 deletions(-) diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java index 4ee4a3c61d..980eeab21d 100644 --- a/src/main/java/client/Client.java +++ b/src/main/java/client/Client.java @@ -52,14 +52,10 @@ import scripting.quest.QuestActionManager; import scripting.quest.QuestScriptManager; import server.TimerManager; import server.life.Monster; -import tools.DatabaseConnection; import tools.PacketCreator; import javax.script.ScriptEngine; import java.net.InetSocketAddress; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.time.Duration; import java.time.Instant; @@ -69,8 +65,6 @@ import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -321,61 +315,6 @@ public class Client extends ChannelInboundHandlerAdapter { return inServerTransition; } - // TODO: Recode to close statements... - // Only used from ban command. - private void loadMacsIfNescessary() throws SQLException { - if (macs.isEmpty()) { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT macs FROM accounts WHERE id = ?")) { - ps.setInt(1, accId); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - for (String mac : rs.getString("macs").split(", ")) { - if (!mac.equals("")) { - macs.add(mac); - } - } - } - } - } - } - } - - public void banMacs() { - try { - loadMacsIfNescessary(); - - List filtered = new LinkedList<>(); - try (Connection con = DatabaseConnection.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("SELECT filter FROM macfilters"); - ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - filtered.add(rs.getString("filter")); - } - } - - try (PreparedStatement ps = con.prepareStatement("INSERT INTO macbans (mac, aid) VALUES (?, ?)")) { - for (String mac : macs) { - boolean matched = false; - for (String filter : filtered) { - if (mac.matches(filter)) { - matched = true; - break; - } - } - if (!matched) { - ps.setString(1, mac); - ps.setString(2, String.valueOf(getAccID())); - ps.executeUpdate(); - } - } - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - } - public void setPin(String pin) { this.pin = pin; } diff --git a/src/main/java/database/account/Account.java b/src/main/java/database/account/Account.java index 64f774fdde..f239645b20 100644 --- a/src/main/java/database/account/Account.java +++ b/src/main/java/database/account/Account.java @@ -2,6 +2,7 @@ package database.account; import client.LoginState; import lombok.Builder; +import net.server.coordinator.session.Hwid; import java.time.Instant; import java.time.LocalDate; @@ -14,7 +15,8 @@ import java.util.Objects; @Builder public record Account(int id, String name, String password, boolean acceptedTos, Byte gender, LocalDate birthdate, String pin, String pic, byte chrSlots, LoginState loginState, LocalDateTime lastLogin, - boolean banned, Instant bannedUntil, Byte banReason, String banDescription) { + boolean banned, Instant bannedUntil, Byte banReason, String banDescription, String ip, + String macs, Hwid hwid) { public Account { Objects.requireNonNull(name); Objects.requireNonNull(password); diff --git a/src/main/java/database/account/AccountRepository.java b/src/main/java/database/account/AccountRepository.java index 57b44d27ba..4d362ca6cf 100644 --- a/src/main/java/database/account/AccountRepository.java +++ b/src/main/java/database/account/AccountRepository.java @@ -11,6 +11,10 @@ import java.util.Optional; * @author Ponk */ public class AccountRepository { + private static final String SELECT_ACCOUNT_COLS = "a.id, a.name, password, pin, pic, birthdate, a.gender, " + + "tos_accepted, chr_slots, login_state, last_login, banned, banned_until, ban_reason, ban_description, " + + "ip, macs, hwid"; + private final PgDatabaseConnection connection; public AccountRepository(PgDatabaseConnection connection) { @@ -19,10 +23,9 @@ public class AccountRepository { public Optional findByNameIgnoreCase(String name) { String sql = """ - SELECT id, name, password, pin, pic, birthdate, gender, tos_accepted, chr_slots, login_state, - last_login, banned, banned_until, ban_reason, ban_description - FROM account - WHERE lower(name) = lower(:name)"""; + SELECT %s + FROM account AS a + WHERE lower(name) = lower(:name)""".formatted(SELECT_ACCOUNT_COLS); try (Handle handle = connection.getHandle()) { return handle.createQuery(sql) .bind("name", name) @@ -33,10 +36,9 @@ public class AccountRepository { public Optional findById(int accountId) { String sql = """ - SELECT id, name, password, pin, pic, birthdate, gender, tos_accepted, chr_slots, login_state, - last_login, banned, banned_until, ban_reason, ban_description - FROM account - WHERE id = :id"""; + SELECT %s + FROM account AS a + WHERE id = :id""".formatted(SELECT_ACCOUNT_COLS); try (Handle handle = connection.getHandle()) { return handle.createQuery(sql) .bind("id", accountId) @@ -45,16 +47,16 @@ public class AccountRepository { } } - public Optional findIdByChrNameIgnoreCase(String name) { + public Optional findByChrNameIgnoreCase(String name) { String sql = """ - SELECT id + SELECT %s FROM account AS a INNER JOIN chr AS c ON a.id = c.account - WHERE lower(c.name) = lower(:name)"""; + WHERE lower(c.name) = lower(:name)""".formatted(SELECT_ACCOUNT_COLS); try (Handle handle = connection.getHandle()) { return handle.createQuery(sql) .bind("name", name) - .mapTo(Integer.class) + .mapTo(Account.class) .findOne(); } } @@ -158,14 +160,14 @@ public class AccountRepository { public boolean setBanned(int accountId, Instant bannedUntil, byte banReason, String description) { String sql = """ UPDATE account - SET banned = true, banned_until = :bannedUntil, ban_reason = :banReason, description = :description + SET banned = true, banned_until = :bannedUntil, ban_reason = :banReason, ban_description = :banDescription WHERE id = :id"""; try (Handle handle = connection.getHandle()) { return handle.createUpdate(sql) .bind("id", accountId) .bind("bannedUntil", bannedUntil) .bind("banReason", banReason) - .bind("description", description) + .bind("banDescription", description) .execute() > 0; } } diff --git a/src/main/java/database/account/AccountRowMapper.java b/src/main/java/database/account/AccountRowMapper.java index b153092268..6d8f336101 100644 --- a/src/main/java/database/account/AccountRowMapper.java +++ b/src/main/java/database/account/AccountRowMapper.java @@ -2,6 +2,7 @@ package database.account; import client.LoginState; import lombok.extern.slf4j.Slf4j; +import net.server.coordinator.session.Hwid; import org.jdbi.v3.core.mapper.RowMapper; import org.jdbi.v3.core.statement.StatementContext; @@ -40,6 +41,11 @@ public class AccountRowMapper implements RowMapper { .orElse(null)) .banReason(rs.getByte("ban_reason")) .banDescription(rs.getString("ban_description")) + .ip(rs.getString("ip")) + .macs(rs.getString("macs")) + .hwid(Optional.ofNullable(rs.getString("hwid")) + .map(Hwid::new) + .orElse(null)) .build(); } diff --git a/src/main/java/database/ban/HwidBanRepository.java b/src/main/java/database/ban/HwidBanRepository.java index 6978bd0219..026e4a7447 100644 --- a/src/main/java/database/ban/HwidBanRepository.java +++ b/src/main/java/database/ban/HwidBanRepository.java @@ -1,10 +1,12 @@ package database.ban; import database.PgDatabaseConnection; +import lombok.extern.slf4j.Slf4j; import org.jdbi.v3.core.Handle; import java.util.List; +@Slf4j public class HwidBanRepository { private final PgDatabaseConnection connection; @@ -22,4 +24,19 @@ public class HwidBanRepository { .list(); } } + + public boolean saveHwidBan(String hwid, int accountId) { + String sql = """ + INSERT INTO hwid_ban (hwid, account_id) + VALUES (:hwid, :accountId)"""; + try (Handle handle = connection.getHandle()) { + return handle.createUpdate(sql) + .bind("hwid", hwid) + .bind("accountId", accountId) + .execute() > 0; + } catch (Exception e) { + log.error("Failed to save hwid ban. The hwid is already banned? accountId: {}, hwid: {}", accountId, hwid, e); + return false; + } + } } diff --git a/src/main/java/server/ban/HwidBanManager.java b/src/main/java/server/ban/HwidBanManager.java index 13c8029bc0..acdcdd9fb0 100644 --- a/src/main/java/server/ban/HwidBanManager.java +++ b/src/main/java/server/ban/HwidBanManager.java @@ -46,4 +46,11 @@ public class HwidBanManager { public synchronized boolean isBanned(Hwid hwid) { return bannedHwids.contains(hwid); } + + public synchronized void banHwid(Hwid hwid, int accountId) { + if (hwid == null) { + throw new IllegalArgumentException("hwid cannot be null"); + } + hwidBanRepository.saveHwidBan(hwid.hwid(), accountId); + } } diff --git a/src/main/java/service/AccountService.java b/src/main/java/service/AccountService.java index ff8ee86c5e..818d77c597 100644 --- a/src/main/java/service/AccountService.java +++ b/src/main/java/service/AccountService.java @@ -91,8 +91,8 @@ public class AccountService { return accountRepository.findById(accountId); } - public Optional getAccountIdByChrName(String chrName) { - return accountRepository.findIdByChrNameIgnoreCase(chrName); + public Optional getAccountIdByChrName(String chrName) { + return accountRepository.findByChrNameIgnoreCase(chrName); } public boolean acceptTos(int accountId) { diff --git a/src/main/java/service/BanService.java b/src/main/java/service/BanService.java index 2da009b894..f529d4f54e 100644 --- a/src/main/java/service/BanService.java +++ b/src/main/java/service/BanService.java @@ -4,6 +4,7 @@ import client.Character; import client.Client; import client.autoban.AutobanFactory; import config.YamlConfig; +import database.account.Account; import lombok.extern.slf4j.Slf4j; import net.packet.Packet; import net.server.Server; @@ -16,6 +17,8 @@ import tools.PacketCreator; import java.time.Duration; import java.time.Instant; +import java.util.Arrays; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -86,8 +89,6 @@ public class BanService { ban(c, victimName, duration, reason, description); } - // TODO: also ban ip and macs. Table "ipbans" and "macbans" (while taking "macfilters" into consideration). - // That's how it was done previously, anyway. private void ban(Client c, String victimName, Duration duration, byte reason, String description) { Character victim = c.getChannelServer().getPlayerStorage().getCharacterByName(victimName); @@ -107,12 +108,16 @@ public class BanService { } private boolean banOfflineChr(String victimName, Duration duration, byte reason, String description) { - Optional foundAccountId = accountService.getAccountIdByChrName(victimName); - if (foundAccountId.isEmpty()) { + Optional foundAccount = accountService.getAccountIdByChrName(victimName); + if (foundAccount.isEmpty()) { return false; } - saveBan(foundAccountId.get(), duration, reason, description); + Account account = foundAccount.get(); + saveBan(account.id(), duration, reason, description); + banIp(account.ip(), account.id()); + banMacs(account.macs(), account.id()); + banHwid(account.hwid(), account.id()); return true; } @@ -122,8 +127,13 @@ public class BanService { String ip = victim.getClient().getRemoteAddress(); String enrichedDescription = "[%s] %s (IP: %s)".formatted(description, readableName, ip); saveBan(victim.getAccountID(), duration, reason, enrichedDescription); + banIp(ip, victim.getAccountID()); + Account victimAccount = victim.getClient().getAccount(); + banMacs(victimAccount.macs(), victim.getAccountID()); + banHwid(victimAccount.hwid(), victim.getAccountID()); + victim.sendPacket(PacketCreator.sendPolice("You have been banned by %s.".formatted(c.getPlayer().getName()))); - TimerManager.getInstance().schedule(() -> transitionService.disconnect(c, false), + TimerManager.getInstance().schedule(() -> transitionService.disconnect(victim.getClient(), true), TimeUnit.SECONDS.toMillis(5)); return true; } @@ -138,6 +148,31 @@ public class BanService { accountService.ban(accountId, bannedUntil, reason, description); } + private void banIp(String ip, int accountId) { + if (ip == null || ip.isEmpty()) { + return; + } + + ipBanManager.banIp(ip, accountId); + } + + private void banMacs(String macs, int accountId) { + if (macs == null || macs.isEmpty()) { + return; + } + + List macsToBan = Arrays.asList(macs.split(", ")); + macsToBan.forEach(mac -> macBanManager.banMac(mac, accountId)); + } + + private void banHwid(Hwid hwid, int accountId) { + if (hwid == null) { + return; + } + + hwidBanManager.banHwid(hwid, accountId); + } + public boolean isBanned(Client c) { return isIpBanned(c) || isHwidBanned(c) || isMacBanned(c); } diff --git a/src/main/java/service/TransitionService.java b/src/main/java/service/TransitionService.java index 0e51e7d8d0..5e668c28ea 100644 --- a/src/main/java/service/TransitionService.java +++ b/src/main/java/service/TransitionService.java @@ -174,7 +174,7 @@ public class TransitionService { } } - SessionCoordinator.getInstance().closeSession(c, false); + SessionCoordinator.getInstance().closeSession(c, shutdown); if (!c.isInTransition() && c.isLoggedIn()) { diff --git a/src/main/resources/db/migration/postgresql/V0.10__ban.sql b/src/main/resources/db/migration/postgresql/V0.10__ban.sql index 51a4e9914c..b5fefc4b34 100644 --- a/src/main/resources/db/migration/postgresql/V0.10__ban.sql +++ b/src/main/resources/db/migration/postgresql/V0.10__ban.sql @@ -14,7 +14,7 @@ CREATE TABLE hwid_ban created_at timestamp DEFAULT now() NOT NULL, PRIMARY KEY (hwid) ); -GRANT SELECT ON TABLE hwid_ban TO ${server-username}; +GRANT SELECT, INSERT ON TABLE hwid_ban TO ${server-username}; CREATE TABLE mac_ban (