You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 15:36:17 +00:00
fix(rcon): validate room ownership and clothing grants
This commit is contained in:
@@ -2,6 +2,7 @@ package com.eu.habbo.messages.rcon;
|
|||||||
|
|
||||||
import com.eu.habbo.Emulator;
|
import com.eu.habbo.Emulator;
|
||||||
import com.eu.habbo.habbohotel.rooms.Room;
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
|
import com.eu.habbo.habbohotel.users.HabboInfo;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
public class ChangeRoomOwner extends RCONMessage<ChangeRoomOwner.JSON> {
|
public class ChangeRoomOwner extends RCONMessage<ChangeRoomOwner.JSON> {
|
||||||
@@ -11,15 +12,33 @@ public class ChangeRoomOwner extends RCONMessage<ChangeRoomOwner.JSON> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(Gson gson, JSON json) {
|
public void handle(Gson gson, JSON json) {
|
||||||
|
if (json.room_id <= 0 || json.user_id <= 0) {
|
||||||
|
this.status = RCONMessage.STATUS_ERROR;
|
||||||
|
this.message = "invalid room or user";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HabboInfo owner = Emulator.getGameEnvironment().getHabboManager().getHabboInfo(json.user_id);
|
||||||
|
if (owner == null) {
|
||||||
|
this.status = RCONMessage.HABBO_NOT_FOUND;
|
||||||
|
this.message = "user not found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Room room = Emulator.getGameEnvironment().getRoomManager().loadRoom(json.room_id);
|
Room room = Emulator.getGameEnvironment().getRoomManager().loadRoom(json.room_id);
|
||||||
|
|
||||||
if (room != null) {
|
if (room == null) {
|
||||||
room.setOwnerId(json.user_id);
|
this.status = RCONMessage.ROOM_NOT_FOUND;
|
||||||
room.setOwnerName(json.username);
|
this.message = "room not found";
|
||||||
room.setNeedsUpdate(true);
|
return;
|
||||||
room.save();
|
|
||||||
Emulator.getGameEnvironment().getRoomManager().unloadRoom(room);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
room.setOwnerId(owner.getId());
|
||||||
|
room.setOwnerName(owner.getUsername());
|
||||||
|
room.setNeedsUpdate(true);
|
||||||
|
room.save();
|
||||||
|
Emulator.getGameEnvironment().getRoomManager().unloadRoom(room);
|
||||||
|
this.message = "updated room owner";
|
||||||
}
|
}
|
||||||
|
|
||||||
static class JSON {
|
static class JSON {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public class GiveUserClothing extends RCONMessage<GiveUserClothing.JSONGiveUserClothing> {
|
public class GiveUserClothing extends RCONMessage<GiveUserClothing.JSONGiveUserClothing> {
|
||||||
@@ -23,14 +24,35 @@ public class GiveUserClothing extends RCONMessage<GiveUserClothing.JSONGiveUserC
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(Gson gson, GiveUserClothing.JSONGiveUserClothing object) {
|
public void handle(Gson gson, GiveUserClothing.JSONGiveUserClothing object) {
|
||||||
|
if (object.user_id <= 0 || object.clothing_id <= 0) {
|
||||||
|
this.status = RCONMessage.STATUS_ERROR;
|
||||||
|
this.message = "invalid user or clothing";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userExists(object.user_id)) {
|
||||||
|
this.status = RCONMessage.HABBO_NOT_FOUND;
|
||||||
|
this.message = "user not found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clothingExists(object.clothing_id)) {
|
||||||
|
this.status = RCONMessage.STATUS_ERROR;
|
||||||
|
this.message = "clothing not found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(object.user_id);
|
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(object.user_id);
|
||||||
|
|
||||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO users_clothing (user_id, clothing_id) VALUES (?, ?)")) {
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT IGNORE INTO users_clothing (user_id, clothing_id) VALUES (?, ?)")) {
|
||||||
statement.setInt(1, object.user_id);
|
statement.setInt(1, object.user_id);
|
||||||
statement.setInt(2, object.clothing_id);
|
statement.setInt(2, object.clothing_id);
|
||||||
statement.execute();
|
statement.executeUpdate();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
LOGGER.error("Caught SQL exception", e);
|
LOGGER.error("Caught SQL exception", e);
|
||||||
|
this.status = RCONMessage.SYSTEM_ERROR;
|
||||||
|
this.message = "failed to grant clothing";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (habbo != null) {
|
if (habbo != null) {
|
||||||
@@ -42,6 +64,39 @@ public class GiveUserClothing extends RCONMessage<GiveUserClothing.JSONGiveUserC
|
|||||||
client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FIGURESET_REDEEMED.key));
|
client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FIGURESET_REDEEMED.key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.message = "granted clothing";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean userExists(int userId) {
|
||||||
|
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId);
|
||||||
|
if (habbo != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement("SELECT id FROM users WHERE id = ? LIMIT 1")) {
|
||||||
|
statement.setInt(1, userId);
|
||||||
|
try (ResultSet set = statement.executeQuery()) {
|
||||||
|
return set.next();
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Caught SQL exception", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean clothingExists(int clothingId) {
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement("SELECT id FROM catalog_clothing WHERE id = ? LIMIT 1")) {
|
||||||
|
statement.setInt(1, clothingId);
|
||||||
|
try (ResultSet set = statement.executeQuery()) {
|
||||||
|
return set.next();
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Caught SQL exception", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class JSONGiveUserClothing {
|
static class JSONGiveUserClothing {
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.eu.habbo.messages.rcon;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class ChangeRoomOwnerContractTest {
|
||||||
|
private static String source() throws Exception {
|
||||||
|
return Files.readString(Path.of("src/main/java/com/eu/habbo/messages/rcon/ChangeRoomOwner.java"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesRoomAndUserBeforeChangingOwnership() throws Exception {
|
||||||
|
String source = source();
|
||||||
|
|
||||||
|
assertTrue(source.contains("json.room_id <= 0 || json.user_id <= 0"),
|
||||||
|
"Room owner changes must reject invalid identifiers");
|
||||||
|
assertTrue(source.contains("getHabboInfo(json.user_id)"),
|
||||||
|
"Room owner changes must resolve the canonical user from storage");
|
||||||
|
assertTrue(source.contains("HABBO_NOT_FOUND"),
|
||||||
|
"Room owner changes must report missing target users");
|
||||||
|
assertTrue(source.contains("ROOM_NOT_FOUND"),
|
||||||
|
"Room owner changes must report missing rooms");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void doesNotTrustClientSuppliedOwnerNames() throws Exception {
|
||||||
|
String source = source();
|
||||||
|
|
||||||
|
assertTrue(source.contains("setOwnerName(owner.getUsername())"),
|
||||||
|
"Room owner changes must use the stored username");
|
||||||
|
assertFalse(source.contains("setOwnerName(json.username)"),
|
||||||
|
"Room owner changes must not trust JSON usernames");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.eu.habbo.messages.rcon;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class GiveUserClothingContractTest {
|
||||||
|
private static String source() throws Exception {
|
||||||
|
return Files.readString(Path.of("src/main/java/com/eu/habbo/messages/rcon/GiveUserClothing.java"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesUsersAndClothingBeforeWritingInventoryRows() throws Exception {
|
||||||
|
String source = source();
|
||||||
|
|
||||||
|
assertTrue(source.contains("object.user_id <= 0 || object.clothing_id <= 0"),
|
||||||
|
"Clothing grants must reject invalid identifiers");
|
||||||
|
assertTrue(source.contains("userExists(object.user_id)"),
|
||||||
|
"Clothing grants must reject missing users before inserting rows");
|
||||||
|
assertTrue(source.contains("clothingExists(object.clothing_id)"),
|
||||||
|
"Clothing grants must reject catalog clothing ids that do not exist");
|
||||||
|
assertTrue(source.contains("catalog_clothing"),
|
||||||
|
"Clothing grants must validate against catalog_clothing");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void handlesDuplicateGrantsWithoutSurfacingSqlErrors() throws Exception {
|
||||||
|
String source = source();
|
||||||
|
|
||||||
|
assertTrue(source.contains("INSERT IGNORE INTO users_clothing"),
|
||||||
|
"Duplicate clothing grants should be idempotent");
|
||||||
|
assertTrue(source.contains("SYSTEM_ERROR"),
|
||||||
|
"Unexpected SQL failures must be surfaced to the RCON caller");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user