-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
20 changed files
with
723 additions
and
20 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
faf-java-server-app/src/main/java/com/faforever/server/game/SpoofDetectorService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package com.faforever.server.game; | ||
|
||
import com.faforever.server.client.ClientService; | ||
import com.faforever.server.entity.Avatar; | ||
import com.faforever.server.entity.AvatarAssociation; | ||
import com.faforever.server.entity.Game; | ||
import com.faforever.server.entity.Player; | ||
import com.faforever.server.entity.Player.FraudReport; | ||
import com.faforever.server.entity.Rating; | ||
import com.faforever.server.error.ErrorCode; | ||
import com.faforever.server.error.Requests; | ||
import com.faforever.server.player.PlayerService; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.Collection; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* When players joins a game, they send their own information (like name and rating) to other players. This information | ||
* can, therefore, not be trusted. Players can send the information they received from others to this service in order | ||
* to have it verified. If this service receives multiple reports of incorrect data, it will tell all peers to | ||
* disconnect the player who spoofs its data. | ||
*/ | ||
@Service | ||
@Slf4j | ||
public class SpoofDetectorService { | ||
|
||
private final PlayerService playerService; | ||
private final ClientService clientService; | ||
|
||
public SpoofDetectorService(PlayerService playerService, ClientService clientService) { | ||
this.playerService = playerService; | ||
this.clientService = clientService; | ||
} | ||
|
||
public void verifyPlayer(Player reporter, int reporteeId, String name, float mean, float deviation, String country, String avatarUrl, String avatarDescription) { | ||
Game reporterGame = reporter.getCurrentGame(); | ||
Requests.verify(reporterGame != null, ErrorCode.THIS_PLAYER_NOT_IN_GAME); | ||
|
||
Optional<Player> reporteeOptional = playerService.getOnlinePlayer(reporteeId); | ||
Requests.verify(reporteeOptional.isPresent(), ErrorCode.PLAYER_NOT_ONLINE, reporteeId); | ||
Player reportee = reporteeOptional.get(); | ||
|
||
Game reporteeGame = reportee.getCurrentGame(); | ||
Requests.verify(reporteeGame != null, ErrorCode.OTHER_PLAYER_NOT_IN_GAME, reporteeId); | ||
|
||
Requests.verify(reporterGame.equals(reporteeGame), ErrorCode.NOT_SAME_GAME, reportee); | ||
|
||
boolean passesValidation = verifyName(reporter, name, reportee) | ||
&& verifyRating(reporter, mean, deviation, reportee) | ||
&& verifyCountry(reporter, country, reportee) | ||
&& verifyAvatar(reporter, avatarUrl, avatarDescription, reportee); | ||
|
||
if (!passesValidation) { | ||
reportee.getFraudReportsByReporterId().put( | ||
reporter.getId(), | ||
new FraudReport(reporter.getId(), reporterGame.getId(), name, (int) mean, (int) deviation, country, avatarDescription, avatarUrl) | ||
); | ||
|
||
int reportedFrauds = reportee.getFraudReportsByReporterId().size(); | ||
if (reportedFrauds > 1 && reportedFrauds >= reporterGame.getConnectedPlayers().size() / 2) { | ||
Collection<Player> peers = reporteeGame.getConnectedPlayers().values().stream() | ||
.filter(player -> !Objects.equals(player.getId(), reporteeId)) | ||
.collect(Collectors.toList()); | ||
|
||
clientService.disconnectPlayerFromGame(reporteeId, peers); | ||
} | ||
} | ||
} | ||
|
||
private boolean verifyAvatar(Player reporter, String avatarUrl, String avatarDescription, Player reportee) { | ||
Optional<Avatar> optionalAvatar = reportee.getAvailableAvatars().stream() | ||
.filter(AvatarAssociation::isSelected) | ||
.map(AvatarAssociation::getAvatar) | ||
.findFirst(); | ||
if (optionalAvatar.isPresent()) { | ||
Avatar avatar = optionalAvatar.get(); | ||
if (!Objects.equals(avatarUrl, avatar.getUrl())) { | ||
log.debug("Avatar URL '{}' of player '{}' does not match in-game URL '{}' as reported by player '{}'", | ||
avatarUrl, reportee, avatar.getUrl(), reporter); | ||
return false; | ||
} | ||
if (!Objects.equals(avatarDescription, avatar.getDescription())) { | ||
log.debug("Avatar description '{}' of player '{}' does not match in-game description '{}' as reported by player '{}'", | ||
avatarDescription, reportee, avatar.getDescription(), reporter); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
private boolean verifyCountry(Player reporter, String country, Player reportee) { | ||
if (!Objects.equals(country, reportee.getCountry())) { | ||
log.debug("Country '{}' of player '{}' does not match in-game country '{}' as reported by player '{}'", | ||
reportee.getCountry(), reportee, country, reporter); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
private boolean verifyRating(Player reporter, float mean, float deviation, Player reportee) { | ||
Rating reporteeRating = reportee.getRatingWithinCurrentGame(); | ||
|
||
if (!Objects.equals(mean, (float) reporteeRating.getMean()) || !Objects.equals(deviation, (float) reporteeRating.getDeviation())) { | ||
log.debug("Rating '{}/{}' of player '{}' does not match in-game rating '{}/{}' as reported by player '{}'", | ||
reporteeRating.getMean(), reporteeRating.getDeviation(), reportee, mean, deviation, reporter); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
private boolean verifyName(Player reporter, String name, Player reportee) { | ||
if (!Objects.equals(name, reportee.getLogin())) { | ||
log.debug("Name of player '{}' does not match in-game name '{}' as reported by player '{}'", reportee, name, reporter); | ||
return false; | ||
} | ||
return true; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
faf-java-server-app/src/main/java/com/faforever/server/game/VerifyPlayerReport.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.faforever.server.game; | ||
|
||
import com.faforever.server.common.ClientMessage; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Data | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class VerifyPlayerReport implements ClientMessage { | ||
private int id; | ||
private String name; | ||
private float mean; | ||
private float deviation; | ||
private String country; | ||
private String avatarUrl; | ||
private String avatarDescription; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
...ver-app/src/main/java/com/faforever/server/integration/SpoofDetectorServiceActivator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.faforever.server.integration; | ||
|
||
import com.faforever.server.entity.Player; | ||
import com.faforever.server.game.SpoofDetectorService; | ||
import com.faforever.server.game.VerifyPlayerReport; | ||
import com.faforever.server.security.FafUserDetails; | ||
import org.springframework.integration.annotation.MessageEndpoint; | ||
import org.springframework.integration.annotation.ServiceActivator; | ||
import org.springframework.messaging.handler.annotation.Header; | ||
import org.springframework.security.core.Authentication; | ||
|
||
import static com.faforever.server.integration.MessageHeaders.USER_HEADER; | ||
|
||
@MessageEndpoint | ||
public class SpoofDetectorServiceActivator { | ||
private final SpoofDetectorService spoofDetectorService; | ||
|
||
public SpoofDetectorServiceActivator(SpoofDetectorService spoofDetectorService) { | ||
this.spoofDetectorService = spoofDetectorService; | ||
} | ||
|
||
@ServiceActivator(inputChannel = ChannelNames.VERIFY_PLAYER_REQUEST) | ||
public void verifyPlayerReport(VerifyPlayerReport report, @Header(USER_HEADER) Authentication authentication) { | ||
spoofDetectorService.verifyPlayer( | ||
getPlayer(authentication), | ||
report.getId(), | ||
report.getName(), | ||
report.getMean(), | ||
report.getDeviation(), | ||
report.getCountry(), | ||
report.getAvatarUrl(), | ||
report.getAvatarDescription() | ||
); | ||
} | ||
|
||
private Player getPlayer(Authentication authentication) { | ||
return ((FafUserDetails) authentication.getPrincipal()).getPlayer(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.