mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-09-07 12:48:03 +02:00
Merge branch 'playerjoin-listener-cleanup' of https://github.com/AuthMe-Team/AuthMeReloaded
This commit is contained in:
commit
4db5062935
@ -29,8 +29,8 @@ public class AntiBot {
|
||||
private AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED;
|
||||
|
||||
@Inject
|
||||
public AntiBot(NewSetting settings, Messages messages, PermissionsManager permissionsManager,
|
||||
BukkitService bukkitService) {
|
||||
AntiBot(NewSetting settings, Messages messages, PermissionsManager permissionsManager,
|
||||
BukkitService bukkitService) {
|
||||
this.settings = settings;
|
||||
this.messages = messages;
|
||||
this.permissionsManager = permissionsManager;
|
||||
@ -86,7 +86,12 @@ public class AntiBot {
|
||||
}, duration * TICKS_PER_MINUTE);
|
||||
}
|
||||
|
||||
public void checkAntiBot(final Player player) {
|
||||
/**
|
||||
* Handles a player joining the server and checks if AntiBot needs to be activated.
|
||||
*
|
||||
* @param player the player who joined the server
|
||||
*/
|
||||
public void handlePlayerJoin(final Player player) {
|
||||
if (antiBotStatus == AntiBotStatus.ACTIVE || antiBotStatus == AntiBotStatus.DISABLED) {
|
||||
return;
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ import fr.xephi.authme.output.Log4JFilter;
|
||||
import fr.xephi.authme.output.MessageKey;
|
||||
import fr.xephi.authme.output.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.SHA256;
|
||||
@ -650,16 +649,6 @@ public class AuthMe extends JavaPlugin {
|
||||
return pluginHooks != null && pluginHooks.isNpc(player) || player.hasMetadata("NPC");
|
||||
}
|
||||
|
||||
// Select the player to kick when a vip player joins the server when full
|
||||
public Player generateKickPlayer(Collection<? extends Player> collection) {
|
||||
for (Player player : collection) {
|
||||
if (!getPermissionsManager().hasPermission(player, PlayerStatePermission.IS_VIP)) {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Purge inactive players from the database, as defined in the configuration
|
||||
private void runAutoPurge() {
|
||||
if (!newSettings.getProperty(PurgeSettings.USE_AUTO_PURGE) || autoPurging) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.command.CommandService;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.converter.Converter;
|
||||
@ -23,9 +22,6 @@ import java.util.List;
|
||||
*/
|
||||
public class ConverterCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private AuthMe authMe;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
|
@ -16,20 +16,24 @@ import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
||||
import org.bukkit.projectiles.ProjectileSource;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static fr.xephi.authme.listener.ListenerService.shouldCancelEvent;
|
||||
|
||||
public class AuthMeEntityListener implements Listener {
|
||||
|
||||
private static Method getShooter;
|
||||
private static boolean shooterIsProjectileSource;
|
||||
private Method getShooter;
|
||||
private boolean shooterIsProjectileSource;
|
||||
|
||||
public AuthMeEntityListener() {
|
||||
try {
|
||||
Method m = Projectile.class.getDeclaredMethod("getShooter");
|
||||
shooterIsProjectileSource = m.getReturnType() != LivingEntity.class;
|
||||
} catch (Exception ignored) {
|
||||
getShooter = Projectile.class.getDeclaredMethod("getShooter");
|
||||
shooterIsProjectileSource = getShooter.getReturnType() != LivingEntity.class;
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
ConsoleLogger.logException("Cannot load getShooter() method on Projectile class", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +91,7 @@ public class AuthMeEntityListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO #568: Need to check this, player can't throw snowball but the item is taken.
|
||||
// TODO #733: Player can't throw snowball but the item is taken.
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onProjectileLaunch(ProjectileLaunchEvent event) {
|
||||
if (event.getEntity() == null) {
|
||||
@ -96,6 +100,7 @@ public class AuthMeEntityListener implements Listener {
|
||||
|
||||
Player player = null;
|
||||
Projectile projectile = event.getEntity();
|
||||
// In old versions of the Bukkit API getShooter() returns a Player object instead of a ProjectileSource
|
||||
if (shooterIsProjectileSource) {
|
||||
ProjectileSource shooter = projectile.getShooter();
|
||||
if (shooter == null || !(shooter instanceof Player)) {
|
||||
@ -103,14 +108,14 @@ public class AuthMeEntityListener implements Listener {
|
||||
}
|
||||
player = (Player) shooter;
|
||||
} else {
|
||||
// TODO #568 20151220: Invoking getShooter() with null but method isn't static
|
||||
try {
|
||||
if (getShooter == null) {
|
||||
getShooter = Projectile.class.getMethod("getShooter");
|
||||
}
|
||||
Object obj = getShooter.invoke(null);
|
||||
Object obj = getShooter.invoke(projectile);
|
||||
player = (Player) obj;
|
||||
} catch (Exception ignored) {
|
||||
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||
ConsoleLogger.logException("Error getting shooter", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,18 @@
|
||||
package fr.xephi.authme.listener;
|
||||
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import fr.xephi.authme.AntiBot;
|
||||
import fr.xephi.authme.AntiBot.AntiBotStatus;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.cache.auth.PlayerAuth;
|
||||
import fr.xephi.authme.cache.auth.PlayerCache;
|
||||
import fr.xephi.authme.cache.limbo.LimboCache;
|
||||
import fr.xephi.authme.cache.limbo.LimboPlayer;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.output.MessageKey;
|
||||
import fr.xephi.authme.output.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.settings.NewSetting;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||
import fr.xephi.authme.settings.properties.ProtectionSettings;
|
||||
import fr.xephi.authme.settings.properties.RegistrationSettings;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import fr.xephi.authme.util.BukkitService;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import fr.xephi.authme.util.ValidationService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@ -54,27 +40,22 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerShearEntityEvent;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static fr.xephi.authme.listener.ListenerService.shouldCancelEvent;
|
||||
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_MOVEMENT_RADIUS;
|
||||
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_ALL_COMMANDS_IF_REGISTRATION_IS_OPTIONAL;
|
||||
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT;
|
||||
|
||||
/**
|
||||
* Listener class for player events.
|
||||
*/
|
||||
public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
public class AuthMePlayerListener implements Listener {
|
||||
|
||||
public static final ConcurrentHashMap<String, String> joinMessage = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
private AuthMe plugin;
|
||||
@Inject
|
||||
private NewSetting settings;
|
||||
@Inject
|
||||
@ -90,47 +71,23 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
private Pattern nicknamePattern;
|
||||
|
||||
private void sendLoginOrRegisterMessage(final Player player) {
|
||||
bukkitService.runTaskAsynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (dataSource.isAuthAvailable(player.getName().toLowerCase())) {
|
||||
m.send(player, MessageKey.LOGIN_MESSAGE);
|
||||
} else {
|
||||
if (settings.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)) {
|
||||
m.send(player, MessageKey.REGISTER_EMAIL_MESSAGE);
|
||||
} else {
|
||||
m.send(player, MessageKey.REGISTER_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
private OnJoinVerifier onJoinVerifier;
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
|
||||
String cmd = event.getMessage().split(" ")[0].toLowerCase();
|
||||
if (settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD) && "/motd".equals(cmd)) {
|
||||
return;
|
||||
}
|
||||
if (!settings.getProperty(RegistrationSettings.FORCE)
|
||||
&& settings.getProperty(ALLOW_ALL_COMMANDS_IF_REGISTRATION_IS_OPTIONAL)) {
|
||||
if (settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD) && cmd.equals("/motd")) {
|
||||
return;
|
||||
}
|
||||
if (settings.getProperty(RestrictionSettings.ALLOW_COMMANDS).contains(cmd)) {
|
||||
return;
|
||||
}
|
||||
if (Utils.checkAuth(event.getPlayer())) {
|
||||
final Player player = event.getPlayer();
|
||||
if (!shouldCancelEvent(player)) {
|
||||
return;
|
||||
}
|
||||
event.setCancelled(true);
|
||||
sendLoginOrRegisterMessage(event.getPlayer());
|
||||
m.send(player, MessageKey.DENIED_COMMAND);
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
@ -142,12 +99,7 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
final Player player = event.getPlayer();
|
||||
if (shouldCancelEvent(player)) {
|
||||
event.setCancelled(true);
|
||||
bukkitService.runTaskAsynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
m.send(player, MessageKey.DENIED_CHAT);
|
||||
}
|
||||
});
|
||||
m.send(player, MessageKey.DENIED_CHAT);
|
||||
} else if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) {
|
||||
Set<Player> recipients = event.getRecipients();
|
||||
Iterator<Player> iter = recipients.iterator();
|
||||
@ -157,6 +109,9 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
if (recipients.size() == 0) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,18 +125,21 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
* Limit player X and Z movements to 1 block
|
||||
* Deny player Y+ movements (allows falling)
|
||||
*/
|
||||
if (event.getFrom().getBlockX() == event.getTo().getBlockX()
|
||||
&& event.getFrom().getBlockZ() == event.getTo().getBlockZ()
|
||||
&& event.getFrom().getY() - event.getTo().getY() >= 0) {
|
||||
Location from = event.getFrom();
|
||||
Location to = event.getTo();
|
||||
if (from.getBlockX() == to.getBlockX()
|
||||
&& from.getBlockZ() == to.getBlockZ()
|
||||
&& from.getY() - to.getY() >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
if (Utils.checkAuth(player)) {
|
||||
if (!shouldCancelEvent(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) {
|
||||
// "cancel" the event
|
||||
event.setTo(event.getFrom());
|
||||
if (settings.getProperty(RestrictionSettings.REMOVE_SPEED)) {
|
||||
player.setFlySpeed(0.0f);
|
||||
@ -234,156 +192,72 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
if (player == null) {
|
||||
return;
|
||||
if (player != null) {
|
||||
// Schedule login task so works after the prelogin
|
||||
// (Fix found by Koolaid5000)
|
||||
bukkitService.runTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
management.performJoin(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Schedule login task so works after the prelogin
|
||||
// (Fix found by Koolaid5000)
|
||||
bukkitService.runTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
management.performJoin(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Note ljacqu 20160528: AsyncPlayerPreLoginEvent is not fired by all servers in offline mode
|
||||
// e.g. CraftBukkit does not. So we need to run crucial things in onPlayerLogin, too
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||
PlayerAuth auth = dataSource.getAuth(event.getName());
|
||||
if (auth == null && antiBot.getAntiBotStatus() == AntiBotStatus.ACTIVE) {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_ANTIBOT));
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
antiBot.antibotKicked.addIfAbsent(event.getName());
|
||||
return;
|
||||
}
|
||||
if (auth == null && settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)) {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE));
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
return;
|
||||
}
|
||||
final String name = event.getName().toLowerCase();
|
||||
if (name.length() > settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH) || name.length() < settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)) {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_LENGTH));
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
return;
|
||||
}
|
||||
if (settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE) && auth != null && auth.getRealName() != null) {
|
||||
String realName = auth.getRealName();
|
||||
if (!realName.isEmpty() && !"Player".equals(realName) && !realName.equals(event.getName())) {
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_CASE, realName, event.getName()));
|
||||
return;
|
||||
}
|
||||
if (realName.isEmpty() || "Player".equals(realName)) {
|
||||
dataSource.updateRealName(event.getName().toLowerCase(), event.getName());
|
||||
}
|
||||
}
|
||||
final boolean isAuthAvailable = dataSource.isAuthAvailable(event.getName());
|
||||
|
||||
if (auth == null && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) {
|
||||
String playerIp = event.getAddress().getHostAddress();
|
||||
if (!validationService.isCountryAdmitted(playerIp)) {
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Player player = bukkitService.getPlayerExact(name);
|
||||
// Check if forceSingleSession is set to true, so kick player that has
|
||||
// joined with same nick of online player
|
||||
if (player != null && settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) {
|
||||
try {
|
||||
// Potential performance improvement: make checkAntiBot not require `isAuthAvailable` info and use
|
||||
// "checkKickNonRegistered" as last -> no need to query the DB before checking antibot / name
|
||||
onJoinVerifier.checkAntibot(name, isAuthAvailable);
|
||||
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
|
||||
onJoinVerifier.checkIsValidName(name);
|
||||
} catch (FailedVerificationException e) {
|
||||
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.USERNAME_ALREADY_ONLINE_ERROR));
|
||||
LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name);
|
||||
if (limbo != null && PlayerCache.getInstance().isAuthenticated(name)) {
|
||||
Utils.addNormal(player, limbo.getGroup());
|
||||
LimboCache.getInstance().deleteLimboPlayer(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerLogin(PlayerLoginEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
if (player == null || Utils.isUnrestricted(player)) {
|
||||
if (Utils.isUnrestricted(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getResult() == PlayerLoginEvent.Result.KICK_FULL) {
|
||||
if (permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) {
|
||||
int playersOnline = bukkitService.getOnlinePlayers().size();
|
||||
if (playersOnline > plugin.getServer().getMaxPlayers()) {
|
||||
event.allow();
|
||||
} else {
|
||||
Player pl = plugin.generateKickPlayer(bukkitService.getOnlinePlayers());
|
||||
if (pl != null) {
|
||||
pl.kickPlayer(m.retrieveSingle(MessageKey.KICK_FOR_VIP));
|
||||
event.allow();
|
||||
} else {
|
||||
ConsoleLogger.info("The player " + event.getPlayer().getName() + " tried to join, but the server was full");
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_FULL_SERVER));
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_FULL_SERVER));
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||
} else if (onJoinVerifier.refusePlayerForFullServer(event)) {
|
||||
return;
|
||||
} else if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String name = player.getName().toLowerCase();
|
||||
boolean isAuthAvailable = dataSource.isAuthAvailable(name);
|
||||
final PlayerAuth auth = dataSource.getAuth(player.getName());
|
||||
final boolean isAuthAvailable = (auth != null);
|
||||
|
||||
if (antiBot.getAntiBotStatus() == AntiBotStatus.ACTIVE && !isAuthAvailable) {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_ANTIBOT));
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
|
||||
antiBot.antibotKicked.addIfAbsent(player.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED) && !isAuthAvailable) {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE));
|
||||
try {
|
||||
onJoinVerifier.checkAntibot(name, isAuthAvailable);
|
||||
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
|
||||
onJoinVerifier.checkIsValidName(name);
|
||||
onJoinVerifier.checkNameCasing(player, auth);
|
||||
onJoinVerifier.checkSingleSession(player);
|
||||
onJoinVerifier.checkPlayerCountry(isAuthAvailable, event);
|
||||
} catch (FailedVerificationException e) {
|
||||
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.length() > settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH) || name.length() < settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)) {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_LENGTH));
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.equalsIgnoreCase("Player") || !nicknamePattern.matcher(player.getName()).matches()) {
|
||||
event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_CHARACTERS)
|
||||
.replace("REG_EX", nicknamePattern.pattern()));
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
|
||||
return;
|
||||
}
|
||||
|
||||
antiBot.checkAntiBot(player);
|
||||
|
||||
if (settings.getProperty(HooksSettings.BUNGEECORD)) {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("IP");
|
||||
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
|
||||
}
|
||||
antiBot.handlePlayerJoin(player);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.getProperty(RegistrationSettings.REMOVE_LEAVE_MESSAGE)) {
|
||||
event.setQuitMessage(null);
|
||||
}
|
||||
@ -399,18 +273,8 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
public void onPlayerKick(PlayerKickEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)
|
||||
&& event.getReason().equals(m.retrieveSingle(MessageKey.USERNAME_ALREADY_ONLINE_ERROR))) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!antiBot.antibotKicked.contains(player.getName())) {
|
||||
plugin.getManagement().performQuit(player, true);
|
||||
management.performQuit(player, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -439,7 +303,7 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
public void onPlayerInventoryOpen(InventoryOpenEvent event) {
|
||||
final Player player = (Player) event.getPlayer();
|
||||
|
||||
if (!ListenerService.shouldCancelEvent(player)) {
|
||||
if (!shouldCancelEvent(player)) {
|
||||
return;
|
||||
}
|
||||
event.setCancelled(true);
|
||||
@ -448,7 +312,7 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
* @note little hack cause InventoryOpenEvent cannot be cancelled for
|
||||
* real, cause no packet is send to server by client for the main inv
|
||||
*/
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
bukkitService.scheduleSyncDelayedTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
player.closeInventory();
|
||||
@ -465,10 +329,7 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
return;
|
||||
}
|
||||
Player player = (Player) event.getWhoClicked();
|
||||
if (Utils.checkAuth(player)) {
|
||||
return;
|
||||
}
|
||||
if (plugin.getPluginHooks().isNpc(player)) {
|
||||
if (!shouldCancelEvent(player)) {
|
||||
return;
|
||||
}
|
||||
event.setCancelled(true);
|
||||
@ -476,7 +337,7 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onPlayerHitPlayerEvent(EntityDamageByEntityEvent event) {
|
||||
if (ListenerService.shouldCancelEvent(event)) {
|
||||
if (shouldCancelEvent(event)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
@ -505,11 +366,12 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onSignChange(SignChangeEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (ListenerService.shouldCancelEvent(player)) {
|
||||
if (shouldCancelEvent(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check this, why do we need to update the quit loc? -sgdc3
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onPlayerRespawn(PlayerRespawnEvent event) {
|
||||
if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) {
|
||||
@ -548,16 +410,4 @@ public class AuthMePlayerListener implements Listener, Reloadable {
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void reload() {
|
||||
String nickRegEx = settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS);
|
||||
try {
|
||||
nicknamePattern = Pattern.compile(nickRegEx);
|
||||
} catch (Exception e) {
|
||||
nicknamePattern = Pattern.compile(".*?");
|
||||
ConsoleLogger.showError("Nickname pattern is not a valid regular expression! "
|
||||
+ "Fallback to allowing all nicknames");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package fr.xephi.authme.listener;
|
||||
|
||||
import fr.xephi.authme.output.MessageKey;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Exception thrown when a verification has failed.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class FailedVerificationException extends Exception {
|
||||
|
||||
private final MessageKey reason;
|
||||
private final String[] args;
|
||||
|
||||
public FailedVerificationException(MessageKey reason, String... args) {
|
||||
this.reason = reason;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public MessageKey getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": reason=" + (reason == null ? "null" : reason)
|
||||
+ ";args=" + (args == null ? "null" : StringUtils.join(", ", args));
|
||||
}
|
||||
}
|
223
src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java
Normal file
223
src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java
Normal file
@ -0,0 +1,223 @@
|
||||
package fr.xephi.authme.listener;
|
||||
|
||||
import fr.xephi.authme.AntiBot;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.cache.auth.PlayerAuth;
|
||||
import fr.xephi.authme.cache.auth.PlayerCache;
|
||||
import fr.xephi.authme.cache.limbo.LimboCache;
|
||||
import fr.xephi.authme.cache.limbo.LimboPlayer;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.output.MessageKey;
|
||||
import fr.xephi.authme.output.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.settings.NewSetting;
|
||||
import fr.xephi.authme.settings.properties.ProtectionSettings;
|
||||
import fr.xephi.authme.settings.properties.RegistrationSettings;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import fr.xephi.authme.util.BukkitService;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import fr.xephi.authme.util.ValidationService;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Service for performing various verifications when a player joins.
|
||||
*/
|
||||
class OnJoinVerifier implements Reloadable {
|
||||
|
||||
@Inject
|
||||
private NewSetting settings;
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
@Inject
|
||||
private Messages messages;
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
@Inject
|
||||
private AntiBot antiBot;
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
@Inject
|
||||
private LimboCache limboCache;
|
||||
@Inject
|
||||
private Server server;
|
||||
|
||||
private Pattern nicknamePattern;
|
||||
|
||||
OnJoinVerifier() { }
|
||||
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void reload() {
|
||||
String nickRegEx = settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS);
|
||||
try {
|
||||
nicknamePattern = Pattern.compile(nickRegEx);
|
||||
} catch (Exception e) {
|
||||
nicknamePattern = Pattern.compile(".*?");
|
||||
ConsoleLogger.showError("Nickname pattern is not a valid regular expression! "
|
||||
+ "Fallback to allowing all nicknames");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Antibot is enabled.
|
||||
*
|
||||
* @param playerName the name of the player (lowercase)
|
||||
* @param isAuthAvailable whether or not the player is registered
|
||||
*/
|
||||
public void checkAntibot(String playerName, boolean isAuthAvailable) throws FailedVerificationException {
|
||||
if (antiBot.getAntiBotStatus() == AntiBot.AntiBotStatus.ACTIVE && !isAuthAvailable) {
|
||||
antiBot.antibotKicked.addIfAbsent(playerName);
|
||||
throw new FailedVerificationException(MessageKey.KICK_ANTIBOT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether non-registered players should be kicked, and if so, whether the player should be kicked.
|
||||
*
|
||||
* @param isAuthAvailable whether or not the player is registered
|
||||
*/
|
||||
public void checkKickNonRegistered(boolean isAuthAvailable) throws FailedVerificationException {
|
||||
if (!isAuthAvailable && settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)) {
|
||||
throw new FailedVerificationException(MessageKey.MUST_REGISTER_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the name adheres to the configured username restrictions.
|
||||
*
|
||||
* @param name the name to verify
|
||||
*/
|
||||
public void checkIsValidName(String name) throws FailedVerificationException {
|
||||
if (name.length() > settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH)
|
||||
|| name.length() < settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)) {
|
||||
throw new FailedVerificationException(MessageKey.INVALID_NAME_LENGTH);
|
||||
}
|
||||
if (!nicknamePattern.matcher(name).matches()) {
|
||||
throw new FailedVerificationException(MessageKey.INVALID_NAME_CHARACTERS, nicknamePattern.pattern());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the case of a full server and verifies if the user's connection should really be refused
|
||||
* by adjusting the event object accordingly. Attempts to kick a non-VIP player to make room if the
|
||||
* joining player is a VIP.
|
||||
*
|
||||
* @param event the login event to verify
|
||||
* @return true if the player's connection should be refused (i.e. the event does not need to be processed
|
||||
* further), false if the player is not refused
|
||||
*/
|
||||
public boolean refusePlayerForFullServer(PlayerLoginEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
if (event.getResult() != PlayerLoginEvent.Result.KICK_FULL) {
|
||||
// Server is not full, no need to do anything
|
||||
return false;
|
||||
} else if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) {
|
||||
// Server is full and player is NOT VIP; set kick message and proceed with kick
|
||||
event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Server is full and player is VIP; attempt to kick a non-VIP player to make room
|
||||
Collection<? extends Player> onlinePlayers = bukkitService.getOnlinePlayers();
|
||||
if (onlinePlayers.size() < server.getMaxPlayers()) {
|
||||
event.allow();
|
||||
return false;
|
||||
}
|
||||
Player nonVipPlayer = generateKickPlayer(onlinePlayers);
|
||||
if (nonVipPlayer != null) {
|
||||
nonVipPlayer.kickPlayer(messages.retrieveSingle(MessageKey.KICK_FOR_VIP));
|
||||
event.allow();
|
||||
return false;
|
||||
} else {
|
||||
ConsoleLogger.info("VIP player " + player.getName() + " tried to join, but the server was full");
|
||||
event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the casing in the username corresponds to the one in the database, if so configured.
|
||||
*
|
||||
* @param player the player to verify
|
||||
* @param auth the auth object associated with the player
|
||||
*/
|
||||
public void checkNameCasing(Player player, PlayerAuth auth) throws FailedVerificationException {
|
||||
if (auth != null && settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)) {
|
||||
String realName = auth.getRealName(); // might be null or "Player"
|
||||
String connectingName = player.getName();
|
||||
|
||||
if (StringUtils.isEmpty(realName) || "Player".equals(realName)) {
|
||||
dataSource.updateRealName(connectingName.toLowerCase(), connectingName);
|
||||
} else if (!realName.equals(connectingName)) {
|
||||
throw new FailedVerificationException(MessageKey.INVALID_NAME_CASE, realName, connectingName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the player's country is admitted if he is not registered.
|
||||
*
|
||||
* @param isAuthAvailable whether or not the user is registered
|
||||
* @param event the login event of the player
|
||||
*/
|
||||
public void checkPlayerCountry(boolean isAuthAvailable,
|
||||
PlayerLoginEvent event) throws FailedVerificationException {
|
||||
if (!isAuthAvailable && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) {
|
||||
String playerIp = event.getAddress().getHostAddress();
|
||||
if (!validationService.isCountryAdmitted(playerIp)) {
|
||||
throw new FailedVerificationException(MessageKey.COUNTRY_BANNED_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player with the same name (case-insensitive) is already playing and refuses the
|
||||
* connection if so configured.
|
||||
*
|
||||
* @param player the player to verify
|
||||
*/
|
||||
public void checkSingleSession(Player player) throws FailedVerificationException {
|
||||
if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player onlinePlayer = bukkitService.getPlayerExact(player.getName());
|
||||
if (onlinePlayer != null) {
|
||||
String name = player.getName().toLowerCase();
|
||||
LimboPlayer limbo = limboCache.getLimboPlayer(name);
|
||||
if (limbo != null && PlayerCache.getInstance().isAuthenticated(name)) {
|
||||
Utils.addNormal(player, limbo.getGroup());
|
||||
limboCache.deleteLimboPlayer(name);
|
||||
}
|
||||
throw new FailedVerificationException(MessageKey.USERNAME_ALREADY_ONLINE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a non-VIP player to kick when a VIP player joins the server when full.
|
||||
*
|
||||
* @param onlinePlayers list of online players
|
||||
* @return the player to kick, or null if none applicable
|
||||
*/
|
||||
private Player generateKickPlayer(Collection<? extends Player> onlinePlayers) {
|
||||
for (Player player : onlinePlayers) {
|
||||
if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ package fr.xephi.authme.output;
|
||||
*/
|
||||
public enum MessageKey {
|
||||
|
||||
DENIED_COMMAND("denied_command"),
|
||||
|
||||
SAME_IP_ONLINE("same_ip_online"),
|
||||
|
||||
DENIED_CHAT("denied_chat"),
|
||||
|
@ -23,12 +23,6 @@ public class RestrictionSettings implements SettingsClass {
|
||||
public static final Property<Boolean> HIDE_CHAT =
|
||||
newProperty("settings.restrictions.hideChat", false);
|
||||
|
||||
@Comment({
|
||||
"Allow unlogged users to use all the commands if registration is not forced!",
|
||||
"WARNING: use this only if you need it!"})
|
||||
public static final Property<Boolean> ALLOW_ALL_COMMANDS_IF_REGISTRATION_IS_OPTIONAL =
|
||||
newProperty("settings.restrictions.allowAllCommandsIfRegistrationIsOptional", false);
|
||||
|
||||
@Comment("Allowed commands for unauthenticated players")
|
||||
public static final Property<List<String>> ALLOW_COMMANDS =
|
||||
newLowercaseListProperty("settings.restrictions.allowCommands",
|
||||
|
@ -69,9 +69,6 @@ settings:
|
||||
allowChat: false
|
||||
# Can not authenticated players see the chat log?
|
||||
hideChat: false
|
||||
# WARNING: use this only if you need it!
|
||||
# Allow unlogged users to use all the commands if registration is not forced!
|
||||
allowAllCommandsIfRegistrationIsOptional: false
|
||||
# Commands allowed when a player is not authenticated
|
||||
allowCommands:
|
||||
- /login
|
||||
|
@ -1,3 +1,4 @@
|
||||
denied_command: '&cIn order to be able to use this command you must be authenticated!'
|
||||
same_ip_online: 'A player with the same IP is already in game!'
|
||||
denied_chat: '&cIn order to be able to chat you must be authenticated!'
|
||||
kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
|
||||
|
@ -164,7 +164,7 @@ public class AntiBotTest {
|
||||
AntiBot antiBot = createListeningAntiBot();
|
||||
|
||||
// when
|
||||
antiBot.checkAntiBot(player);
|
||||
antiBot.handlePlayerJoin(player);
|
||||
|
||||
// then
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -194,7 +194,7 @@ public class AntiBotTest {
|
||||
AntiBot antiBot = createListeningAntiBot();
|
||||
|
||||
// when
|
||||
antiBot.checkAntiBot(player);
|
||||
antiBot.handlePlayerJoin(player);
|
||||
|
||||
// then
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
419
src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java
Normal file
419
src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java
Normal file
@ -0,0 +1,419 @@
|
||||
package fr.xephi.authme.listener;
|
||||
|
||||
import fr.xephi.authme.AntiBot;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
import fr.xephi.authme.cache.auth.PlayerAuth;
|
||||
import fr.xephi.authme.cache.limbo.LimboCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.output.MessageKey;
|
||||
import fr.xephi.authme.output.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.settings.NewSetting;
|
||||
import fr.xephi.authme.settings.properties.RegistrationSettings;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import fr.xephi.authme.util.BukkitService;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.ValidationService;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Test for {@link OnJoinVerifier}.
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class OnJoinVerifierTest {
|
||||
|
||||
@InjectMocks
|
||||
private OnJoinVerifier onJoinVerifier;
|
||||
|
||||
@Mock
|
||||
private NewSetting settings;
|
||||
@Mock
|
||||
private DataSource dataSource;
|
||||
@Mock
|
||||
private Messages messages;
|
||||
@Mock
|
||||
private PermissionsManager permissionsManager;
|
||||
@Mock
|
||||
private AntiBot antiBot;
|
||||
@Mock
|
||||
private ValidationService validationService;
|
||||
@Mock
|
||||
private BukkitService bukkitService;
|
||||
@Mock
|
||||
private LimboCache limboCache;
|
||||
@Mock
|
||||
private Server server;
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpLogger() {
|
||||
TestHelper.setupLogger();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotDoAnythingForNormalEvent() {
|
||||
// given
|
||||
PlayerLoginEvent event = mock(PlayerLoginEvent.class);
|
||||
given(event.getResult()).willReturn(PlayerLoginEvent.Result.ALLOWED);
|
||||
|
||||
// when
|
||||
boolean result = onJoinVerifier.refusePlayerForFullServer(event);
|
||||
|
||||
// then
|
||||
assertThat(result, equalTo(false));
|
||||
verify(event).getResult();
|
||||
verifyNoMoreInteractions(event);
|
||||
verifyZeroInteractions(bukkitService);
|
||||
verifyZeroInteractions(dataSource);
|
||||
verifyZeroInteractions(permissionsManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRefuseNonVipPlayerForFullServer() {
|
||||
// given
|
||||
Player player = mock(Player.class);
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, "hostname", null);
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
|
||||
given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(false);
|
||||
String serverFullMessage = "server is full";
|
||||
given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)).willReturn(serverFullMessage);
|
||||
|
||||
// when
|
||||
boolean result = onJoinVerifier.refusePlayerForFullServer(event);
|
||||
|
||||
// then
|
||||
assertThat(result, equalTo(true));
|
||||
assertThat(event.getResult(), equalTo(PlayerLoginEvent.Result.KICK_FULL));
|
||||
assertThat(event.getKickMessage(), equalTo(serverFullMessage));
|
||||
verifyZeroInteractions(bukkitService);
|
||||
verifyZeroInteractions(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldKickNonVipForJoiningVipPlayer() {
|
||||
// given
|
||||
Player player = mock(Player.class);
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, "hostname", null);
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
|
||||
given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(true);
|
||||
List<Player> onlinePlayers = Arrays.asList(mock(Player.class), mock(Player.class));
|
||||
given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true);
|
||||
given(permissionsManager.hasPermission(onlinePlayers.get(1), PlayerStatePermission.IS_VIP)).willReturn(false);
|
||||
returnOnlineListFromBukkitServer(onlinePlayers);
|
||||
given(server.getMaxPlayers()).willReturn(onlinePlayers.size());
|
||||
given(messages.retrieveSingle(MessageKey.KICK_FOR_VIP)).willReturn("kick for vip");
|
||||
|
||||
// when
|
||||
boolean result = onJoinVerifier.refusePlayerForFullServer(event);
|
||||
|
||||
// then
|
||||
assertThat(result, equalTo(false));
|
||||
assertThat(event.getResult(), equalTo(PlayerLoginEvent.Result.ALLOWED));
|
||||
// First player is VIP, so expect no interactions there and second player to have been kicked
|
||||
verifyZeroInteractions(onlinePlayers.get(0));
|
||||
verify(onlinePlayers.get(1)).kickPlayer("kick for vip");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldKickVipPlayerIfNoPlayerCanBeKicked() {
|
||||
// given
|
||||
Player player = mock(Player.class);
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, "hostname", null);
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
|
||||
given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(true);
|
||||
List<Player> onlinePlayers = Collections.singletonList(mock(Player.class));
|
||||
given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true);
|
||||
returnOnlineListFromBukkitServer(onlinePlayers);
|
||||
given(server.getMaxPlayers()).willReturn(onlinePlayers.size());
|
||||
given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)).willReturn("kick full server");
|
||||
|
||||
// when
|
||||
boolean result = onJoinVerifier.refusePlayerForFullServer(event);
|
||||
|
||||
// then
|
||||
assertThat(result, equalTo(true));
|
||||
assertThat(event.getResult(), equalTo(PlayerLoginEvent.Result.KICK_FULL));
|
||||
assertThat(event.getKickMessage(), equalTo("kick full server"));
|
||||
verifyZeroInteractions(onlinePlayers.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldKickNonRegistered() throws FailedVerificationException {
|
||||
// given
|
||||
given(settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)).willReturn(true);
|
||||
|
||||
// expect
|
||||
expectValidationExceptionWith(MessageKey.MUST_REGISTER_MESSAGE);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkKickNonRegistered(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotKickRegisteredPlayer() throws FailedVerificationException {
|
||||
// given
|
||||
given(settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)).willReturn(true);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkKickNonRegistered(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotKickUnregisteredPlayer() throws FailedVerificationException {
|
||||
// given
|
||||
given(settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)).willReturn(false);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkKickNonRegistered(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowValidName() throws FailedVerificationException {
|
||||
// given
|
||||
given(settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)).willReturn(4);
|
||||
given(settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH)).willReturn(8);
|
||||
given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+");
|
||||
onJoinVerifier.reload(); // @PostConstruct method
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkIsValidName("Bobby5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRejectTooLongName() throws FailedVerificationException {
|
||||
// given
|
||||
given(settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)).willReturn(4);
|
||||
given(settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH)).willReturn(8);
|
||||
given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+");
|
||||
onJoinVerifier.reload(); // @PostConstruct method
|
||||
|
||||
// expect
|
||||
expectValidationExceptionWith(MessageKey.INVALID_NAME_LENGTH);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkIsValidName("longerthaneight");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRejectTooShortName() throws FailedVerificationException {
|
||||
// given
|
||||
given(settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)).willReturn(4);
|
||||
given(settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH)).willReturn(8);
|
||||
given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+");
|
||||
onJoinVerifier.reload(); // @PostConstruct method
|
||||
|
||||
// expect
|
||||
expectValidationExceptionWith(MessageKey.INVALID_NAME_LENGTH);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkIsValidName("abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRejectNameWithInvalidCharacters() throws FailedVerificationException {
|
||||
// given
|
||||
given(settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)).willReturn(4);
|
||||
given(settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH)).willReturn(8);
|
||||
given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+");
|
||||
onJoinVerifier.reload(); // @PostConstruct method
|
||||
|
||||
// expect
|
||||
expectValidationExceptionWith(MessageKey.INVALID_NAME_CHARACTERS, "[a-zA-Z0-9]+");
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkIsValidName("Tester!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowProperlyCasedName() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("Bobby");
|
||||
PlayerAuth auth = PlayerAuth.builder().name("bobby").realName("Bobby").build();
|
||||
given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(true);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkNameCasing(player, auth);
|
||||
|
||||
// then
|
||||
verifyZeroInteractions(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRejectNameWithWrongCasing() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("Tester");
|
||||
PlayerAuth auth = PlayerAuth.builder().name("tester").realName("testeR").build();
|
||||
given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(true);
|
||||
|
||||
// expect
|
||||
expectValidationExceptionWith(MessageKey.INVALID_NAME_CASE, "testeR", "Tester");
|
||||
|
||||
// when / then
|
||||
onJoinVerifier.checkNameCasing(player, auth);
|
||||
verifyZeroInteractions(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUpdateMissingRealName() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("Authme");
|
||||
PlayerAuth auth = PlayerAuth.builder().name("authme").realName("").build();
|
||||
given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(true);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkNameCasing(player, auth);
|
||||
|
||||
// then
|
||||
verify(dataSource).updateRealName("authme", "Authme");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUpdateDefaultRealName() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("SOMEONE");
|
||||
PlayerAuth auth = PlayerAuth.builder().name("someone").realName("Player").build();
|
||||
given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(true);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkNameCasing(player, auth);
|
||||
|
||||
// then
|
||||
verify(dataSource).updateRealName("someone", "SOMEONE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptCasingMismatchForDisabledSetting() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("Test");
|
||||
PlayerAuth auth = PlayerAuth.builder().name("test").realName("TEST").build();
|
||||
given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(false);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkNameCasing(player, auth);
|
||||
|
||||
// then
|
||||
verifyZeroInteractions(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptNameForUnregisteredAccount() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("MyPlayer");
|
||||
PlayerAuth auth = null;
|
||||
given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(true);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkNameCasing(player, auth);
|
||||
|
||||
// then
|
||||
verifyZeroInteractions(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptNameThatIsNotOnline() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("bobby");
|
||||
given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(true);
|
||||
given(bukkitService.getPlayerExact("bobby")).willReturn(null);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkSingleSession(player);
|
||||
|
||||
// then
|
||||
verifyZeroInteractions(limboCache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRejectNameAlreadyOnline() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("Charlie");
|
||||
Player onlinePlayer = newPlayerWithName("charlie");
|
||||
given(bukkitService.getPlayerExact("Charlie")).willReturn(onlinePlayer);
|
||||
given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(true);
|
||||
|
||||
// expect
|
||||
expectValidationExceptionWith(MessageKey.USERNAME_ALREADY_ONLINE_ERROR);
|
||||
|
||||
// when / then
|
||||
onJoinVerifier.checkSingleSession(player);
|
||||
verify(limboCache).getLimboPlayer("charlie");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptAlreadyOnlineNameForDisabledSetting() throws FailedVerificationException {
|
||||
// given
|
||||
Player player = newPlayerWithName("Felipe");
|
||||
given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(false);
|
||||
|
||||
// when
|
||||
onJoinVerifier.checkSingleSession(player);
|
||||
|
||||
// then
|
||||
verifyZeroInteractions(bukkitService);
|
||||
verifyZeroInteractions(limboCache);
|
||||
}
|
||||
|
||||
private static Player newPlayerWithName(String name) {
|
||||
Player player = mock(Player.class);
|
||||
given(player.getName()).willReturn(name);
|
||||
return player;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void returnOnlineListFromBukkitServer(Collection<Player> onlineList) {
|
||||
// Note ljacqu 20160529: The compiler gets lost in generics because Collection<? extends Player> is returned
|
||||
// from getOnlinePlayers(). We need to uncheck onlineList to a simple Collection or it will refuse to compile.
|
||||
given(bukkitService.getOnlinePlayers()).willReturn((Collection) onlineList);
|
||||
}
|
||||
|
||||
private void expectValidationExceptionWith(MessageKey messageKey, String... args) {
|
||||
//expectedException.expect(FailedVerificationException.class);
|
||||
expectedException.expect(exceptionWithData(messageKey, args));
|
||||
}
|
||||
|
||||
private static Matcher<FailedVerificationException> exceptionWithData(final MessageKey messageKey,
|
||||
final String... args) {
|
||||
return new TypeSafeMatcher<FailedVerificationException>() {
|
||||
@Override
|
||||
protected boolean matchesSafely(FailedVerificationException item) {
|
||||
return messageKey.equals(item.getReason()) && Arrays.equals(args, item.getArgs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendValue("VerificationFailedException: reason=" + messageKey + ";args="
|
||||
+ (args == null ? "null" : StringUtils.join(", ", args)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user