From e02fd0879ac732779f6f28bc271e90131ccce542 Mon Sep 17 00:00:00 2001 From: Yannik Bretschneider Date: Wed, 7 Jul 2021 14:47:54 +0200 Subject: [PATCH] feat: implemented proper filtering of EndRoundRequests --- .../marvelous/server/lobby/Lobby.java | 2 + .../FilterEndRoundRequestSegment.java | 46 +++++++++++++ .../server/lobby/pipelining/Packet.java | 5 ++ .../FilterEndRoundRequestSegmentTest.java | 66 +++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegment.java create mode 100644 Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegmentTest.java diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/Lobby.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/Lobby.java index 93e7eda..d61e0b0 100644 --- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/Lobby.java +++ b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/Lobby.java @@ -63,12 +63,14 @@ public class Lobby { var reqSegment = new RequestGameStateSegment(this.game); this.pauseSegment = new PauseSegment(); + var filterEndRoundRequestSegment = new FilterEndRoundRequestSegment(this::getActivePlayer); var requestTurnEndSegment = new RequestTurnEndSegment(this.game); var disconnectSegment = new DisconnectSegment(this); var gameLogicSegment = new GameLogicSegment(this.game); pipeline.addSegment(reqSegment) .addSegment(pauseSegment) + .addSegment(filterEndRoundRequestSegment) .addSegment(requestTurnEndSegment) .addSegment(disconnectSegment) .addSegment(gameLogicSegment); diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegment.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegment.java new file mode 100644 index 0000000..d9db7c1 --- /dev/null +++ b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegment.java @@ -0,0 +1,46 @@ +package uulm.teamname.marvelous.server.lobby.pipelining; + +import org.tinylog.Logger; +import uulm.teamname.marvelous.gamelibrary.events.Event; +import uulm.teamname.marvelous.gamelibrary.requests.RequestType; +import uulm.teamname.marvelous.server.lobbymanager.Participant; +import uulm.teamname.marvelous.server.lobby.Lobby; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +/** + * {@link Segment} that checks for an {@link RequestType#EndRoundRequest}, and + * if it exists, checks for the player that has sent it. If the player sending + * this request is not the active player, it flags the message as an error. + */ +public class FilterEndRoundRequestSegment implements Segment { + + private final Supplier activeParticipantCallback; + + /** + * Creates a new {@link FilterEndRoundRequestSegment} + * @param activeParticipantCallback is a {@link Supplier}<{@link Participant}> + * that supplies the currently active participant + * in a {@link Lobby}. It should normally be bound + * to {@link Lobby#getActivePlayer()}, by executing + * {@code new Filter...Segment(this::getActivePlayer)}. + */ + public FilterEndRoundRequestSegment(Supplier activeParticipantCallback) { + this.activeParticipantCallback = activeParticipantCallback; + + } + + @Override + public void processRequests(Packet packet, List carrier, AtomicBoolean abort) { + Logger.trace("FilterEndRoundSegment has received {} requests", packet.size()); + if (packet.containsRequestOfType(RequestType.EndRoundRequest)) { + Logger.trace("Packet contains EndRoundRequest"); + if (!packet.getOrigin().equals(activeParticipantCallback.get())) { + Logger.debug("Packet contained EndRoundRequest but was sent from non-active participant, aborting..."); + abort.set(true); + } + } + } +} diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Packet.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Packet.java index 948eb00..649209f 100644 --- a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Packet.java +++ b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Packet.java @@ -68,6 +68,11 @@ public class Packet extends ArrayList { return Objects.equals(origin, packet.origin); } + @Override + public Object clone() { + return new Packet(this.toArray(new Request[0]), this.origin); + } + @Override public int hashCode() { return Objects.hash(super.hashCode(), origin); diff --git a/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegmentTest.java b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegmentTest.java new file mode 100644 index 0000000..fce0c81 --- /dev/null +++ b/Server/src/test/java/uulm/teamname/marvelous/server/lobby/pipelining/FilterEndRoundRequestSegmentTest.java @@ -0,0 +1,66 @@ +package uulm.teamname.marvelous.server.lobby.pipelining; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import uulm.teamname.marvelous.gamelibrary.requests.Request; +import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder; +import uulm.teamname.marvelous.gamelibrary.requests.RequestType; +import uulm.teamname.marvelous.server.lobbymanager.Participant; + +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.*; + +class FilterEndRoundRequestSegmentTest { + + FilterEndRoundRequestSegment segment; + + Participant activeParticipant; + + Request[] requests; + + @BeforeEach + void beforeEach() { + this.segment = new FilterEndRoundRequestSegment(this::getActiveParticipant); + this.activeParticipant = mock(Participant.class); + requests = new Request[] { + new RequestBuilder(RequestType.EndRoundRequest).buildGameRequest() + }; + } + + private Participant getActiveParticipant() { + return activeParticipant; + } + + @Test + @DisplayName("Request from active participant doesn't get filtered") + void packetFromActiveParticipantTest() { + var packet = new Packet(requests, activeParticipant); + var atomicBoolean = new AtomicBoolean(false); + + var processedPacket = (Packet) packet.clone(); + segment.processRequests(processedPacket, new ArrayList<>(), atomicBoolean); + + assertThat(processedPacket).isEqualTo(packet); + assertThat(atomicBoolean.get()).isFalse(); + } + + @Test + @DisplayName("Request from non-active participant gets flagged as an error") + void packetFromNonActiveParticipantTest() { + var packet = new Packet(requests, mock(Participant.class)); + var atomicBoolean = new AtomicBoolean(false); + + var processedPacket = (Packet) packet.clone(); + segment.processRequests(processedPacket, new ArrayList<>(), atomicBoolean); + + // assertThat(processedPacket).isEqualTo(packet); is not necessary as there's no actual filtering going on + assertThat(atomicBoolean.get()).isTrue(); + } + + + +}