Ban ip, macs & hwid in PG

Finally rid of all db code in Client
This commit is contained in:
P0nk
2024-10-03 18:48:19 +02:00
parent 2b6ef9feb5
commit 40425ac4e1
10 changed files with 94 additions and 86 deletions

View File

@@ -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<String> 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;
}

View File

@@ -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);

View File

@@ -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<Account> 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<Account> 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<Integer> findIdByChrNameIgnoreCase(String name) {
public Optional<Account> 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;
}
}

View File

@@ -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<Account> {
.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();
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -91,8 +91,8 @@ public class AccountService {
return accountRepository.findById(accountId);
}
public Optional<Integer> getAccountIdByChrName(String chrName) {
return accountRepository.findIdByChrNameIgnoreCase(chrName);
public Optional<Account> getAccountIdByChrName(String chrName) {
return accountRepository.findByChrNameIgnoreCase(chrName);
}
public boolean acceptTos(int accountId) {

View File

@@ -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<Integer> foundAccountId = accountService.getAccountIdByChrName(victimName);
if (foundAccountId.isEmpty()) {
Optional<Account> 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<String> 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);
}

View File

@@ -174,7 +174,7 @@ public class TransitionService {
}
}
SessionCoordinator.getInstance().closeSession(c, false);
SessionCoordinator.getInstance().closeSession(c, shutdown);
if (!c.isInTransition() && c.isLoggedIn()) {

View File

@@ -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
(