#1350 Add option to force using the sync PlayerLoginEvent

- Introduce new configuration (taken from @sgdc3)
- Create JoiningPlayer, based on a Player object or String name, determining how permissions will be checked
This commit is contained in:
ljacqu
2017-11-01 21:02:22 +01:00
parent a5542051f4
commit 44a7baff9a
9 changed files with 180 additions and 55 deletions
@@ -0,0 +1,66 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.entity.Player;
import java.util.function.BiFunction;
/**
* Represents a player joining the server, which depending on the available
* information may be his name or the actual Player object.
*/
public final class JoiningPlayer {
private final String name;
private final BiFunction<PermissionsManager, PermissionNode, Boolean> permissionLookupFunction;
/**
* Hidden constructor.
*
* @param name the player's name
* @param permFunction the function to use for permission lookups
*/
private JoiningPlayer(String name, BiFunction<PermissionsManager, PermissionNode, Boolean> permFunction) {
this.name = name;
this.permissionLookupFunction = permFunction;
}
/**
* Creates a {@link JoiningPlayer} instance from the given name.
*
* @param name the player's name
* @return the created instance
*/
public static JoiningPlayer fromName(String name) {
return new JoiningPlayer(name, (manager, perm) -> manager.hasPermissionOffline(name, perm));
}
/**
* Creates a {@link JoiningPlayer} instance from the given Player object.
*
* @param player the player
* @return the created instance
*/
public static JoiningPlayer fromPlayerObject(Player player) {
return new JoiningPlayer(player.getName(), (manager, perm) -> manager.hasPermission(player, perm));
}
/**
* @return the player's name
*/
public String getName() {
return name;
}
/**
* Returns the function to use for permission lookups. Takes two arguments: the PermissionsManager instance,
* and the permission node to look up. The result is a boolean indicating whether or not this joining player
* has permission.
*
* @return the permissions lookup function to use
*/
public BiFunction<PermissionsManager, PermissionNode, Boolean> getPermissionLookupFunction() {
return permissionLookupFunction;
}
}
@@ -64,16 +64,16 @@ public class OnJoinVerifier implements Reloadable {
/**
* Checks if Antibot is enabled.
*
* @param name the player name
* @param joiningPlayer the joining player to check
* @param isAuthAvailable whether or not the player is registered
* @throws FailedVerificationException if the verification fails
*/
public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVerificationException {
if (isAuthAvailable || permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)) {
public void checkAntibot(JoiningPlayer joiningPlayer, boolean isAuthAvailable) throws FailedVerificationException {
if (isAuthAvailable || permissionsManager.hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT)) {
return;
}
if (antiBotService.shouldKick()) {
antiBotService.addPlayerKick(name);
antiBotService.addPlayerKick(joiningPlayer.getName());
throw new FailedVerificationException(MessageKey.KICK_ANTIBOT);
}
}
@@ -149,7 +149,7 @@ public class OnJoinVerifier implements Reloadable {
* Checks that the casing in the username corresponds to the one in the database, if so configured.
*
* @param connectingName the player name to verify
* @param auth the auth object associated with the player
* @param auth the auth object associated with the player
* @throws FailedVerificationException if the verification fails
*/
public void checkNameCasing(String connectingName, PlayerAuth auth) throws FailedVerificationException {
@@ -167,15 +167,16 @@ public class OnJoinVerifier implements Reloadable {
/**
* Checks that the player's country is admitted.
*
* @param name the player name
* @param joiningPlayer the joining player to verify
* @param address the player address
* @param isAuthAvailable whether or not the user is registered
* @throws FailedVerificationException if the verification fails
*/
public void checkPlayerCountry(String name, String address, boolean isAuthAvailable) throws FailedVerificationException {
public void checkPlayerCountry(JoiningPlayer joiningPlayer, String address,
boolean isAuthAvailable) throws FailedVerificationException {
if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED))
&& settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)
&& !permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_COUNTRY_CHECK)
&& !permissionsManager.hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_COUNTRY_CHECK)
&& !validationService.isCountryAdmitted(address)) {
throw new FailedVerificationException(MessageKey.COUNTRY_BANNED_ERROR);
}
@@ -14,6 +14,7 @@ import fr.xephi.authme.service.ValidationService;
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.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.ChatColor;
@@ -83,7 +84,7 @@ public class PlayerListener implements Listener {
@Inject
private PermissionsManager permissionsManager;
private static boolean isAsyncPlayerPreLoginEventCalled = false;
private boolean isAsyncPlayerPreLoginEventCalled = false;
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
@@ -206,8 +207,9 @@ public class PlayerListener implements Listener {
teleportationService.teleportNewPlayerToFirstSpawn(player);
}
private void runOnJoinChecks(String name, String ip) throws FailedVerificationException {
private void runOnJoinChecks(JoiningPlayer joiningPlayer, String ip) throws FailedVerificationException {
// Fast stuff
final String name = joiningPlayer.getName();
onJoinVerifier.checkSingleSession(name);
onJoinVerifier.checkIsValidName(name);
@@ -216,9 +218,9 @@ public class PlayerListener implements Listener {
final PlayerAuth auth = dataSource.getAuth(name);
final boolean isAuthAvailable = auth != null;
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
onJoinVerifier.checkAntibot(name, isAuthAvailable);
onJoinVerifier.checkAntibot(joiningPlayer, isAuthAvailable);
onJoinVerifier.checkNameCasing(name, auth);
onJoinVerifier.checkPlayerCountry(name, ip, isAuthAvailable);
onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, isAuthAvailable);
}
// Note #831: AsyncPlayerPreLoginEvent is not fired by all servers in offline mode
@@ -230,6 +232,9 @@ public class PlayerListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onAsyncPlayerPreLoginEvent(AsyncPlayerPreLoginEvent event) {
isAsyncPlayerPreLoginEventCalled = true;
if (!settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT)) {
return;
}
final String name = event.getName();
@@ -245,7 +250,7 @@ public class PlayerListener implements Listener {
}
try {
runOnJoinChecks(name, event.getAddress().getHostAddress());
runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress());
} catch (FailedVerificationException e) {
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
@@ -271,9 +276,9 @@ public class PlayerListener implements Listener {
return;
}
if (!isAsyncPlayerPreLoginEventCalled) {
if (!isAsyncPlayerPreLoginEventCalled || !settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT)) {
try {
runOnJoinChecks(name, event.getAddress().getHostAddress());
runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress());
} catch (FailedVerificationException e) {
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
@@ -2,6 +2,7 @@ package fr.xephi.authme.permission;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.listener.JoiningPlayer;
import fr.xephi.authme.permission.handlers.BPermissionsHandler;
import fr.xephi.authme.permission.handlers.LuckPermsHandler;
import fr.xephi.authme.permission.handlers.PermissionHandler;
@@ -76,7 +77,7 @@ public class PermissionsManager implements Reloadable {
*/
@PostConstruct
private void setup() {
if(settings.getProperty(PluginSettings.FORCE_VAULT_HOOK)) {
if (settings.getProperty(PluginSettings.FORCE_VAULT_HOOK)) {
try {
PermissionHandler handler = createPermissionHandler(PermissionsSystemType.VAULT);
if (handler != null) {
@@ -86,7 +87,7 @@ public class PermissionsManager implements Reloadable {
return;
}
} catch (PermissionHandlerException e) {
e.printStackTrace();
ConsoleLogger.logException("Failed to create Vault hook (forced):", e);
}
} else {
// Loop through all the available permissions system types
@@ -229,6 +230,17 @@ public class PermissionsManager implements Reloadable {
return player.hasPermission(permissionNode.getNode());
}
/**
* Check if the given player has permission for the given permission node.
*
* @param joiningPlayer The player to check
* @param permissionNode The permission node to verify
* @return true if the player has permission, false otherwise
*/
public boolean hasPermission(JoiningPlayer joiningPlayer, PermissionNode permissionNode) {
return joiningPlayer.getPermissionLookupFunction().apply(this, permissionNode);
}
/**
* Check if a player has permission for the given permission node. This is for offline player checks.
* If no permissions system is used, then the player will not have permission.
@@ -82,6 +82,14 @@ public final class PluginSettings implements SettingsHolder {
public static final Property<Boolean> USE_ASYNC_TASKS =
newProperty("settings.useAsyncTasks", true);
@Comment({
"By default we handle the AsyncPlayerPreLoginEvent which makes the plugin faster",
"but it is incompatible with any permission plugin not included in our compatibility list.",
"If you have issues with permission checks on player join please disable this option."
})
public static final Property<Boolean> USE_ASYNC_PRE_LOGIN_EVENT =
newProperty("settings.useAsyncPreLoginEvent", true);
private PluginSettings() {
}