diff --git a/pom.xml b/pom.xml
index a9398738..f4041180 100644
--- a/pom.xml
+++ b/pom.xml
@@ -436,6 +436,14 @@
true
+
+
+ org.spigotmc
+ spigot-api
+ 1.9.2-R0.1-SNAPSHOT
+ provided
+
+
org.bukkit
diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java
index 626ed2a6..647484cd 100644
--- a/src/main/java/fr/xephi/authme/AuthMe.java
+++ b/src/main/java/fr/xephi/authme/AuthMe.java
@@ -26,6 +26,7 @@ import fr.xephi.authme.listener.AuthMeInventoryPacketAdapter;
import fr.xephi.authme.listener.AuthMePlayerListener;
import fr.xephi.authme.listener.AuthMePlayerListener16;
import fr.xephi.authme.listener.AuthMePlayerListener18;
+import fr.xephi.authme.listener.AuthMePlayerListener19;
import fr.xephi.authme.listener.AuthMeServerListener;
import fr.xephi.authme.listener.AuthMeTabCompletePacketAdapter;
import fr.xephi.authme.listener.AuthMeTablistPacketAdapter;
@@ -392,6 +393,13 @@ public class AuthMe extends JavaPlugin {
pluginManager.registerEvents(initializer.get(AuthMePlayerListener18.class), this);
} catch (ClassNotFoundException ignore) {
}
+
+ // Try to register 1.9 player listeners
+ try {
+ Class.forName("org.spigotmc.event.player.PlayerSpawnLocationEvent");
+ pluginManager.registerEvents(initializer.get(AuthMePlayerListener19.class), this);
+ } catch (ClassNotFoundException ignore) {
+ }
}
private void reloadSupportHook() {
diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java
index d61ea002..d57581ff 100644
--- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java
+++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java
@@ -21,7 +21,7 @@ public class RegisterAdminCommand implements ExecutableCommand {
@Inject
private PasswordSecurity passwordSecurity;
-
+
@Inject
private DataSource dataSource;
@@ -65,7 +65,13 @@ public class RegisterAdminCommand implements ExecutableCommand {
ConsoleLogger.info(sender.getName() + " registered " + playerName);
Player player = commandService.getPlayer(playerName);
if (player != null) {
- player.kickPlayer("An admin just registered you, please log in again");
+ final Player p = player;
+ p.getServer().getScheduler().scheduleSyncDelayedTask(commandService.getAuthMe(), new Runnable() {
+ @Override
+ public void run() {
+ p.kickPlayer("An admin just registered you, please log in again");
+ }
+ });
}
}
});
diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java
index 97383c53..a0fa4011 100644
--- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java
+++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java
@@ -20,11 +20,12 @@ import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
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.GeoLiteAPI;
import fr.xephi.authme.util.Utils;
+import fr.xephi.authme.util.ValidationService;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
@@ -85,6 +86,8 @@ public class AuthMePlayerListener implements Listener {
private BukkitService bukkitService;
@Inject
private SpawnLoader spawnLoader;
+ @Inject
+ private ValidationService validationService;
private void handleChat(AsyncPlayerChatEvent event) {
if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) {
@@ -265,30 +268,22 @@ public class AuthMePlayerListener implements Listener {
PlayerAuth auth = dataSource.getAuth(event.getName());
if (settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE) && auth != null && auth.getRealName() != null) {
String realName = auth.getRealName();
- if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) {
+ 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() || realName.equals("Player")) {
+ if (realName.isEmpty() || "Player".equals(realName)) {
dataSource.updateRealName(event.getName().toLowerCase(), event.getName());
}
}
- if (auth == null) {
- if (!Settings.countriesBlacklist.isEmpty() || !Settings.countries.isEmpty()) {
- String playerIP = event.getAddress().getHostAddress();
- String countryCode = GeoLiteAPI.getCountryCode(playerIP);
- if (Settings.countriesBlacklist.contains(countryCode)) {
- event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
- event.setKickMessage(m.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR));
- return;
- }
- if (Settings.enableProtection && !Settings.countries.contains(countryCode)) {
- event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
- event.setKickMessage(m.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR));
- return;
- }
+ 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;
}
}
diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener19.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener19.java
new file mode 100644
index 00000000..8be48ba2
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener19.java
@@ -0,0 +1,27 @@
+package fr.xephi.authme.listener;
+
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.spigotmc.event.player.PlayerSpawnLocationEvent;
+
+import fr.xephi.authme.AuthMe;
+
+import javax.inject.Inject;
+
+/**
+ * Listener of player events for events introduced in Minecraft 1.9.
+ */
+public class AuthMePlayerListener19 implements Listener {
+
+ @Inject
+ private AuthMe plugin;
+
+ public AuthMePlayerListener19() { }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onPlayerSpawn(PlayerSpawnLocationEvent event) {
+ event.setSpawnLocation(plugin.getSpawnLocation(event.getPlayer()));
+ }
+
+}
diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java
index 54df067e..bb85374d 100644
--- a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java
+++ b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java
@@ -5,9 +5,10 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
-import fr.xephi.authme.settings.Settings;
+import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.SpawnLoader;
-import fr.xephi.authme.util.GeoLiteAPI;
+import fr.xephi.authme.settings.properties.ProtectionSettings;
+import fr.xephi.authme.util.ValidationService;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -26,19 +27,19 @@ public class AuthMeServerListener implements Listener {
@Inject
private Messages messages;
@Inject
+ private NewSetting settings;
+ @Inject
private PluginHooks pluginHooks;
@Inject
private SpawnLoader spawnLoader;
+ @Inject
+ private ValidationService validationService;
@EventHandler(priority = EventPriority.HIGHEST)
public void onServerPing(ServerListPingEvent event) {
- if (!Settings.countriesBlacklist.isEmpty() || !Settings.countries.isEmpty()){
- String countryCode = GeoLiteAPI.getCountryCode(event.getAddress().getHostAddress());
- if( Settings.countriesBlacklist.contains(countryCode)) {
- event.setMotd(messages.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR));
- return;
- }
- if (Settings.enableProtection && !Settings.countries.contains(countryCode)) {
+ if (settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) {
+ String playerIp = event.getAddress().getHostAddress();
+ if (!validationService.isCountryAdmitted(playerIp)) {
event.setMotd(messages.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR));
}
}
diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java
index d13668d5..17b4a60a 100644
--- a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java
+++ b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java
@@ -8,6 +8,7 @@ import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldAccessException;
+import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction;
import com.comphenix.protocol.wrappers.PlayerInfoData;
@@ -82,7 +83,12 @@ public class AuthMeTablistPacketAdapter extends PacketAdapter {
}
public void register() {
- ProtocolLibrary.getProtocolManager().addPacketListener(this);
+ if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) {
+ ProtocolLibrary.getProtocolManager().addPacketListener(this);
+ } else {
+ ConsoleLogger.info("The hideTablist feature is not compatible with your minecraft version");
+ ConsoleLogger.info("It requires 1.8+. Disabling the hideTablist feature...");
+ }
}
public void unregister() {
diff --git a/src/main/java/fr/xephi/authme/util/ValidationService.java b/src/main/java/fr/xephi/authme/util/ValidationService.java
index 815f493f..999e2d1c 100644
--- a/src/main/java/fr/xephi/authme/util/ValidationService.java
+++ b/src/main/java/fr/xephi/authme/util/ValidationService.java
@@ -5,7 +5,9 @@ import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.settings.NewSetting;
+import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.EmailSettings;
+import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import org.bukkit.command.CommandSender;
@@ -54,26 +56,74 @@ public class ValidationService {
return null;
}
+ /**
+ * Verifies whether the email is valid and admitted for use according to the plugin settings.
+ *
+ * @param email the email to verify
+ * @return true if the email is valid, false otherwise
+ */
public boolean validateEmail(String email) {
if (!email.contains("@") || "your@email.com".equalsIgnoreCase(email)) {
return false;
}
final String emailDomain = email.split("@")[1];
-
- List whitelist = settings.getProperty(EmailSettings.DOMAIN_WHITELIST);
- if (!CollectionUtils.isEmpty(whitelist)) {
- return containsIgnoreCase(whitelist, emailDomain);
- }
-
- List blacklist = settings.getProperty(EmailSettings.DOMAIN_BLACKLIST);
- return CollectionUtils.isEmpty(blacklist) || !containsIgnoreCase(blacklist, emailDomain);
+ return validateWhitelistAndBlacklist(
+ emailDomain, EmailSettings.DOMAIN_WHITELIST, EmailSettings.DOMAIN_BLACKLIST);
}
+ /**
+ * Queries the database whether the email is still free for registration, i.e. whether the given
+ * command sender may use the email to register a new account (as defined by settings and permissions).
+ *
+ * @param email the email to verify
+ * @param sender the command sender
+ * @return true if the email may be used, false otherwise (registration threshold has been exceeded)
+ */
public boolean isEmailFreeForRegistration(String email, CommandSender sender) {
return permissionsManager.hasPermission(sender, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
|| dataSource.countAuthsByEmail(email) < settings.getProperty(EmailSettings.MAX_REG_PER_EMAIL);
}
+ /**
+ * Checks whether the player's country is allowed to join the server, based on the given IP address
+ * and the configured country whitelist or blacklist.
+ *
+ * @param hostAddress the IP address to verify
+ * @return true if the IP address' country is allowed, false otherwise
+ */
+ public boolean isCountryAdmitted(String hostAddress) {
+ // Check if we have restrictions on country, if not return true and avoid the country lookup
+ if (settings.getProperty(ProtectionSettings.COUNTRIES_WHITELIST).isEmpty()
+ && settings.getProperty(ProtectionSettings.COUNTRIES_BLACKLIST).isEmpty()) {
+ return true;
+ }
+
+ String countryCode = GeoLiteAPI.getCountryCode(hostAddress);
+ return validateWhitelistAndBlacklist(countryCode,
+ ProtectionSettings.COUNTRIES_WHITELIST,
+ ProtectionSettings.COUNTRIES_BLACKLIST);
+ }
+
+ /**
+ * Verifies whether the given value is allowed according to the given whitelist and blacklist settings.
+ * Whitelist has precedence over blacklist: if a whitelist is set, the value is rejected if not present
+ * in the whitelist.
+ *
+ * @param value the value to verify
+ * @param whitelist the whitelist property
+ * @param blacklist the blacklist property
+ * @return true if the value is admitted by the lists, false otherwise
+ */
+ private boolean validateWhitelistAndBlacklist(String value, Property> whitelist,
+ Property> blacklist) {
+ List whitelistValue = settings.getProperty(whitelist);
+ if (!CollectionUtils.isEmpty(whitelistValue)) {
+ return containsIgnoreCase(whitelistValue, value);
+ }
+ List blacklistValue = settings.getProperty(blacklist);
+ return CollectionUtils.isEmpty(blacklistValue) || !containsIgnoreCase(blacklistValue, value);
+ }
+
private static boolean containsIgnoreCase(Collection coll, String needle) {
for (String entry : coll) {
if (entry.equalsIgnoreCase(needle)) {
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java
index 3ce93fcf..89fc3da6 100644
--- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java
@@ -8,7 +8,6 @@ import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -19,13 +18,10 @@ import org.mockito.runners.MockitoJUnitRunner;
import java.util.Arrays;
-import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -147,8 +143,6 @@ public class RegisterAdminCommandTest {
given(dataSource.saveAuth(any(PlayerAuth.class))).willReturn(true);
HashedPassword hashedPassword = new HashedPassword("$aea2345EW235dfsa@#R%987048");
given(passwordSecurity.computeHash(password, user)).willReturn(hashedPassword);
- Player player = mock(Player.class);
- given(commandService.getPlayer(user)).willReturn(player);
// when
command.executeCommand(sender, Arrays.asList(user, password), commandService);
@@ -161,7 +155,6 @@ public class RegisterAdminCommandTest {
verify(dataSource).saveAuth(captor.capture());
assertAuthHasInfo(captor.getValue(), user, hashedPassword);
verify(dataSource).setUnlogged(user);
- verify(player).kickPlayer(argThat(containsString("please log in again")));
}
private void assertAuthHasInfo(PlayerAuth auth, String name, HashedPassword hashedPassword) {