diff --git a/pom.xml b/pom.xml index 95c8b79..d0bebd1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.lielamar 2fa - 1.5.4 + 1.5.5 UTF-8 @@ -42,7 +42,7 @@ 1.4.200 8.0.26 2.7.3 - 42.2.23 + 42.3.1 3.12.10 2.0.0-alpha5 @@ -153,7 +153,7 @@ org.json json - 20200518 + 20210307 compile @@ -168,7 +168,7 @@ net.md-5 bungeecord-api - 1.16-R0.4-SNAPSHOT + 1.16-R0.4 jar provided diff --git a/src/main/java/com/lielamar/auth/bukkit/handlers/AuthHandler.java b/src/main/java/com/lielamar/auth/bukkit/handlers/AuthHandler.java index a00a5c3..9e007cb 100644 --- a/src/main/java/com/lielamar/auth/bukkit/handlers/AuthHandler.java +++ b/src/main/java/com/lielamar/auth/bukkit/handlers/AuthHandler.java @@ -12,10 +12,7 @@ import com.lielamar.lielsutils.ColorUtils; import com.lielamar.lielsutils.SpigotUtils; import net.md_5.bungee.api.chat.*; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -110,23 +107,32 @@ public boolean approveKey(UUID uuid, Integer code) { public void playerJoin(UUID uuid) { super.playerJoin(uuid); - // If no player has joined the server yet, meaning there was no opportunity to check if the server is using bungeecord - // * we check if the server is using bungeecord by sending a message to bungeecord and expecting a response. Once we get a response we know the server is using bungeecord - // then we want to firstly send the #loadBungeecord message to bungeecord, and only then load the player. - // If bungeecord was already loaded (we know this through loadedBungeecord, which changes to true after the initial load), we only want to load the player because we already know whether - // the server is using bungeecord or not. + // Loads the player whenever they join the server. + // + // If bungeecord was not loaded yet, we want to try to load it. What it generally means is - we send a request called + // "LOAD_BUNGEECORD" to bungeecord. If we get a response, it means bungeecord exists, and then we set the value of + // isBungeecordEnabled to true, so we know to use bungeecord for future requests. + // In case bungeecord was loaded successfully, we provide a callback to re-load the joined player, since when we loaded + // them, we did so locally on the spigot instance and not in bungeecord. + // + // This way, if a player was online and then joined a spigot instance that was just booted, it would load their data and ask + // them to authenticate, but straight after that, it'd load bungeecord and then re-load the player and auto-authenticate them. + + handlePlayerJoin(uuid); + if(!loadedBungeecord) { loadedBungeecord = true; - Bukkit.getScheduler().runTaskLater(this.main, () -> this.main.getPluginMessageListener().loadBungeecord(uuid, null), 1L); + long timeMillis = System.currentTimeMillis(); + Bukkit.getScheduler().runTaskLater(this.main, () -> this.main.getPluginMessageListener().loadBungeecord(uuid, new Callback() { + public void execute() { handlePlayerJoin(uuid); } + public long getExecutionStamp() { return timeMillis; } + }), 1L); } - - // Loading the player, only after the above code is done executing (since it's not async). - handlePlayerJoin(uuid); } /** - * A util method to handle the player join event. It removes the map copies from their inventory, loads their auth state (from either spigot/bungeecord), etc. + * A method to handle the player join event. It removes the map copies from their inventory, loads their auth state (from either spigot/bungeecord), etc. * * @param uuid UUID of the player to handle */ @@ -135,9 +141,8 @@ private void handlePlayerJoin(UUID uuid) { Bukkit.getScheduler().runTaskLater(this.main, () -> { Player player = Bukkit.getPlayer(uuid); - if(player == null || !player.isOnline()) { + if(player == null || !player.isOnline()) return; - } // Removing previous QR codes for(ItemStack item : player.getInventory().getContents()) { @@ -200,7 +205,6 @@ public void changeState(UUID uuid, AuthState authState, boolean updateBungeecord PlayerStateChangeEvent event = new PlayerStateChangeEvent(player, authStates.get(uuid), authState); Bukkit.getPluginManager().callEvent(event); - if(event.isCancelled()) return; diff --git a/src/main/java/com/lielamar/auth/bukkit/handlers/BungeecordMessageHandler.java b/src/main/java/com/lielamar/auth/bukkit/handlers/BungeecordMessageHandler.java index 73782b9..f8cd8d7 100644 --- a/src/main/java/com/lielamar/auth/bukkit/handlers/BungeecordMessageHandler.java +++ b/src/main/java/com/lielamar/auth/bukkit/handlers/BungeecordMessageHandler.java @@ -15,7 +15,6 @@ import java.io.*; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.UUID; @@ -23,41 +22,30 @@ public class BungeecordMessageHandler extends PluginMessagingHandler implements PluginMessageListener { private final TwoFactorAuthentication main; - private final Map callbackFunctions; + private final Map callbacks; public BungeecordMessageHandler(TwoFactorAuthentication main) { this.main = main; - this.callbackFunctions = new HashMap<>(); + this.callbacks = new HashMap<>(); - // Looping over the callbacks, if it's been more than 15 seconds cancel the callback + // Removes all callbacks that were set more than 5 seconds ago, since they're most-likely invalid by now Bukkit.getScheduler().runTaskTimerAsynchronously(main, () -> { long currentTimestamp = System.currentTimeMillis(); - - Iterator iterator = callbackFunctions.keySet().iterator(); - UUID key; - Callback value; - while(iterator.hasNext()) { - key = iterator.next(); - value = callbackFunctions.get(key); - - if(value != null) { - if((currentTimestamp - value.getExecutionStamp())/1000 > 15) - iterator.remove(); - } - } - }, 300L, 300L); + callbacks.entrySet().removeIf(entry -> (currentTimestamp - entry.getValue().getExecutionStamp())/1000 > 5); + }, 100L, 100L); } + /** * Sets the header of a message * - * @param msg Message Stream - * @param uuid UUID of the player attached to the message - * @param callback Callback function to call once a response is received + * @param msg Message Stream + * @param uuid UUID of the player attached to the message + * @param callbackUUID UUID of the callback function to call once a response is received */ - public void setMessageHeader(ByteArrayDataOutput msg, UUID uuid, Callback callback) { + public void setMessageHeader(ByteArrayDataOutput msg, UUID uuid, UUID callbackUUID) { msg.writeUTF(super.subChannelName); // Setting the SubChannel of the message - msg.writeUTF(attachCallbackFunction(callback).toString()); // Setting the Message UUID + msg.writeUTF(callbackUUID.toString()); // Setting the Message UUID msg.writeUTF(uuid.toString()); // Setting the UUID of the player } @@ -91,6 +79,20 @@ public void applyMessageBody(ByteArrayDataOutput msg, ByteArrayOutputStream msgB msg.write(msgBody.toByteArray()); // Setting the body data } + /** + * Generates a random UUID for the message and attaches the callback function to call it later when a response is made + * + * @param callback Callback function to save + * @return Random generated UUID + */ + public UUID registerCallback(Callback callback) { + UUID randomUUID = UUID.randomUUID(); + + if(callback != null) + this.callbacks.put(randomUUID, callback); + return randomUUID; + } + /** * Sends the message to bungeecord * @@ -105,20 +107,6 @@ public void sendMessage(UUID uuid, ByteArrayDataOutput msg) { } - /** - * Generates a random UUID for the message and attaches the callback function to call it later when a response is made - * - * @param callback Callback function to save - * @return Random generated UUID - */ - public UUID attachCallbackFunction(Callback callback) { - UUID randomUUID = UUID.randomUUID(); - if(callback != null) - this.callbackFunctions.put(randomUUID, callback); - return randomUUID; - } - - /** * Communicates with BungeeCord and sets the player authentication state to {authenticated} * @@ -128,7 +116,7 @@ public UUID attachCallbackFunction(Callback callback) { */ public void setBungeeCordAuthState(UUID uuid, AuthHandler.AuthState state, Callback callback) { ByteArrayDataOutput msg = ByteStreams.newDataOutput(); - this.setMessageHeader(msg, uuid, callback); + this.setMessageHeader(msg, uuid, registerCallback(callback)); ByteArrayOutputStream msgBody = new ByteArrayOutputStream(); this.setMessageBody(msgBody, MessageAction.SET_STATE, state.name()); @@ -150,7 +138,7 @@ public void setBungeeCordAuthState(UUID uuid, AuthHandler.AuthState state) { */ public void getBungeeCordAuthState(UUID uuid, AuthHandler.AuthState defaultState, Callback callback) { ByteArrayDataOutput msg = ByteStreams.newDataOutput(); - this.setMessageHeader(msg, uuid, callback); + this.setMessageHeader(msg, uuid, registerCallback(callback)); ByteArrayOutputStream msgBody = new ByteArrayOutputStream(); this.setMessageBody(msgBody, MessageAction.GET_STATE, defaultState.name()); @@ -163,10 +151,15 @@ public void getBungeeCordAuthState(UUID uuid, AuthHandler.AuthState defaultState this.getBungeeCordAuthState(uuid, defaultState, null); } - + /** + * Loads Bungeecord (checks if bungeecord exists in the server) + * + * @param uuid UUID of the player to use to load the bungeecord + * @param callback A callback function to call whenever a response for the message is received + */ public void loadBungeecord(UUID uuid, Callback callback) { ByteArrayDataOutput msg = ByteStreams.newDataOutput(); - this.setMessageHeader(msg, uuid, callback); + this.setMessageHeader(msg, uuid, registerCallback(callback)); ByteArrayOutputStream msgBody = new ByteArrayOutputStream(); this.setMessageBody(msgBody, MessageAction.LOAD_BUNGEECORD); @@ -213,7 +206,7 @@ public void onPluginMessageReceived(String channel, @NotNull Player player, @Not main.getAuthHandler().changeState(playerUUID, state, false); } - Callback callback = this.callbackFunctions.getOrDefault(messageUUID, null); + Callback callback = this.callbacks.getOrDefault(messageUUID, null); if(callback != null) callback.execute(); } catch (IOException | IllegalArgumentException exception) { exception.printStackTrace(); diff --git a/src/main/java/com/lielamar/auth/bukkit/handlers/DependencyHandler.java b/src/main/java/com/lielamar/auth/bukkit/handlers/DependencyHandler.java index ab3690f..acbe3bd 100644 --- a/src/main/java/com/lielamar/auth/bukkit/handlers/DependencyHandler.java +++ b/src/main/java/com/lielamar/auth/bukkit/handlers/DependencyHandler.java @@ -26,8 +26,6 @@ public DependencyHandler(Plugin plugin) { } catch(Exception exception) { Bukkit.getServer().getConsoleSender().sendMessage(ChatColor.RED + "[2FA] 2FA detected that you are using Java 16 without the --add-opens java.base/java.lang=ALL-UNNAMED or the --add-opens java.base/java.net=ALL-UNNAMED flags!"); Bukkit.getServer().getConsoleSender().sendMessage(ChatColor.RED + "[2FA] If you want the plugin to support all features, most significantly Remote Databases, please add this flag to your startup script"); -// Bukkit.getServer().getConsoleSender().sendMessage(ChatColor.RED + "[2FA] Disabling plugin..."); -// Bukkit.getPluginManager().disablePlugin(plugin); } } @@ -63,7 +61,7 @@ private void loadDependencies(Plugin plugin, Properties properties) { String h2Version = properties.getProperty("version_h2", "1.4.200"); String mysqlVersion = properties.getProperty("version_mysql", "8.0.26"); String mariaDBVersion = properties.getProperty("version_maria_db", "2.7.3"); - String postgresVersion = properties.getProperty("version_postgres", "42.2.23"); + String postgresVersion = properties.getProperty("version_postgres", "42.3.1"); String mongoDBVersion = properties.getProperty("version_mongo_db", "3.12.10"); String slf4jVersion = properties.getProperty("version_slf4j", "2.0.0-alpha5"); diff --git a/src/main/resources/bungee.yml b/src/main/resources/bungee.yml index 10dbf9b..3828c56 100644 --- a/src/main/resources/bungee.yml +++ b/src/main/resources/bungee.yml @@ -1,5 +1,5 @@ name: 2FA -version: "1.5.4" +version: "1.5.5" author: "LielAmar" main: com.lielamar.auth.bungee.TwoFactorAuthentication description: Add another layer of protection to your server \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 7da8cb3..d79cfdc 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: 2FA -version: "1.5.4" +version: "1.5.5" authors: [LielAmar, SadGhost] main: com.lielamar.auth.bukkit.TwoFactorAuthentication description: Add another layer of protection to your server @@ -12,7 +12,7 @@ libraries: - com.h2database:h2:1.4.200 - mysql:mysql-connector-java:8.0.26 - org.mariadb.jdbc:mariadb-java-client:2.7.3 - - org.postgresql:postgresql:42.2.23 + - org.postgresql:postgresql:42.3.1 - org.mongodb:mongo-java-driver:3.12.10 - org.slf4j:slf4j-api:2.0.0-alpha5