diff --git a/SECURITY.md b/SECURITY.md index 12dab535..adc91e1d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -19,13 +19,13 @@ These versions are no longer under active development, however we will look to r | Version | Supported | Support End: | | ------------------- | ------------------ | ------------ | | 2021.04 | :white_check_mark: | July 2021 | -| 2021.02 | :white_check_mark: | June 2021 | ### No Longer Supported These versions are no longer supported at all. It is strongly advised to update if you are running any of these versions. | Version | Supported | Support Ended: | | ------------------- | ------------------ | ------------------- | +| 2021.02 | :x: | 6 June 2021 | | 2020.11 | :x: | 3 May 2021 | | 6.0.x (Pre-Release) | :x: | December 2020 | | < 2020.11 | :x: | December 2020 | diff --git a/pom.xml b/pom.xml index 83483900..ba09abf3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ me.totalfreedom TotalFreedomMod - 2021.04 + 2021.05 jar @@ -39,6 +39,11 @@ + + apache-snapshots + https://repository.apache.org/content/repositories/snapshots/ + + jitpack.io https://jitpack.io @@ -109,6 +114,11 @@ ess-repo https://ci.ender.zone/plugin/repository/everything/ + + + mattmalec-repo + https://repo.mattmalec.com/repository/releases + @@ -144,7 +154,7 @@ org.bstats bstats-bukkit - 1.8 + 2.2.1 compile @@ -186,21 +196,21 @@ net.dv8tion JDA - 4.2.1_255 + 4.3.0_277 provided net.coreprotect coreprotect - 19.3 + 19.5 provided com.sk89q.worldguard worldguard-bukkit - 7.0.4 + 7.0.5 provided @@ -259,12 +269,26 @@ 3.1.1 compile + + + com.mattmalec + Pterodactyl4J + 2.BETA_47 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0-SNAPSHOT + + org.junit.jupiter junit-jupiter 5.4.2 compile + org.eclipse.sisu org.eclipse.sisu.inject @@ -299,7 +323,7 @@ pl.project13.maven git-commit-id-plugin - 4.0.2 + 4.0.5 get-the-git-infos @@ -412,10 +436,12 @@ + org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.3.0-SNAPSHOT package @@ -427,8 +453,7 @@ io.papermc.lib - me.totalfreedom.totalfreedommod.paperlib - + me.totalfreedom.totalfreedommod.paperlib org.bstats @@ -446,6 +471,7 @@ com.github.speedxx:Mojangson org.bstats:bstats-bukkit org.jetbrains:annotations + com.mattmalec:Pterodactyl4J diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Pterodactyl.java b/src/main/java/me/totalfreedom/totalfreedommod/Pterodactyl.java index 12c0cefd..91a78ef3 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/Pterodactyl.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/Pterodactyl.java @@ -1,26 +1,30 @@ package me.totalfreedom.totalfreedommod; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; +import com.mattmalec.pterodactyl4j.Permission; +import com.mattmalec.pterodactyl4j.PowerAction; +import com.mattmalec.pterodactyl4j.PteroAction; +import com.mattmalec.pterodactyl4j.PteroBuilder; +import com.mattmalec.pterodactyl4j.application.entities.ApplicationUser; +import com.mattmalec.pterodactyl4j.application.entities.PteroApplication; +import com.mattmalec.pterodactyl4j.application.managers.UserAction; +import com.mattmalec.pterodactyl4j.client.entities.ClientServer; +import com.mattmalec.pterodactyl4j.client.entities.ClientSubuser; +import com.mattmalec.pterodactyl4j.client.entities.PteroClient; import joptsimple.internal.Strings; import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.config.ConfigEntry; import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.util.FLog; -import me.totalfreedom.totalfreedommod.util.FUtil; -import me.totalfreedom.totalfreedommod.util.Response; -import org.json.simple.JSONObject; -import org.json.simple.parser.ParseException; public class Pterodactyl extends FreedomService { - public final String URL = ConfigEntry.PTERO_URL.getString(); - private final String SERVER_KEY = ConfigEntry.PTERO_SERVER_KEY.getString(); private final String ADMIN_KEY = ConfigEntry.PTERO_ADMIN_KEY.getString(); - private final List SERVER_HEADERS = Arrays.asList("Accept:Application/vnd.pterodactyl.v1+json", "Content-Type:application/json", "Authorization:Bearer " + SERVER_KEY); - private final List ADMIN_HEADERS = Arrays.asList("Accept:Application/vnd.pterodactyl.v1+json", "Content-Type:application/json", "Authorization:Bearer " + ADMIN_KEY); + private final String CLIENT_KEY = ConfigEntry.PTERO_CLIENT_KEY.getString(); + private final String IDENTIFIER = ConfigEntry.PTERO_SERVER_UUID.getString(); + private final PteroApplication adminAPI = PteroBuilder.createApplication(URL, ADMIN_KEY); + private final PteroClient clientAPI = PteroBuilder.createClient(URL, CLIENT_KEY); + private final ClientServer server = clientAPI.retrieveServerByIdentifier(IDENTIFIER).execute(); private boolean enabled = !Strings.isNullOrEmpty(URL); @@ -43,163 +47,60 @@ public class Pterodactyl extends FreedomService if (!admin.isActive() || admin.getRank() != Rank.SENIOR_ADMIN) { - FLog.debug("Disabling ptero acc"); + FLog.debug("Disabling Pterodactyl account"); removeAccountFromServer(id); return; } - FLog.debug("Enabling ptero acc"); + FLog.debug("Enabling Pterodactyl account"); addAccountToServer(id); } - @SuppressWarnings("unchecked") public String createAccount(String username, String password) { - JSONObject json = new JSONObject(); - json.put("username", username); - json.put("password", password); - json.put("email", username.toLowerCase() + "@" + ConfigEntry.PTERO_DEFAULT_EMAIL_DOMAIN.getString()); - json.put("first_name", username); - json.put("last_name", "\u200E"); // required, so I made it appear empty - - Response response; - JSONObject jsonResponse; - try - { - response = FUtil.sendRequest(URL + "/api/application/users", "POST", ADMIN_HEADERS, json.toJSONString()); - jsonResponse = response.getJSONMessage(); - } - catch (IOException | ParseException e) - { - FLog.severe(e); - return null; - } - - return ((JSONObject)jsonResponse.get("attributes")).get("id").toString(); + UserAction action = adminAPI.getUserManager().createUser() + .setUserName(username) + .setEmail(username.toLowerCase() + "@" + ConfigEntry.PTERO_DEFAULT_EMAIL_DOMAIN.getString()) + .setFirstName(username) + .setLastName("\u200E") // Required - make it appear empty + .setPassword(password); + return action.execute().getId(); } - public boolean deleteAccount(String id) + public void deleteAccount(String id) { - JSONObject json = new JSONObject(); - try - { - return FUtil.sendRequest(URL + "/api/application/users/" + id, "DELETE", ADMIN_HEADERS, json.toJSONString()).getCode() == 204; - } - catch (IOException e) - { - FLog.severe(e); - return false; - } + ApplicationUser username = adminAPI.retrieveUserById(id).execute(); + PteroAction action = adminAPI.getUserManager().deleteUser(username); + action.execute(); } - @SuppressWarnings("unchecked") public void addAccountToServer(String id) { - String url = URL + "/api/client/servers/" + ConfigEntry.PTERO_SERVER_UUID.getString() + "/users"; - - JSONObject userData = getUserData(id); - if (userData == null) - { - FLog.severe("The Pterodactyl user with the ID of " + id + " was not found"); - return; - } - - JSONObject json = new JSONObject(); - json.put("email", userData.get("email").toString()); - json.put("permissions", Arrays.asList("control.console", "control.start", "control.restart", "control.stop", "control.kill")); - - try - { - FUtil.sendRequest(url, "POST", SERVER_HEADERS, json.toJSONString()); - } - catch (IOException e) - { - FLog.severe(e); - } + ApplicationUser username = adminAPI.retrieveUserById(id).execute(); + String email = username.getEmail(); + PteroAction server = clientAPI.retrieveServerByIdentifier(IDENTIFIER); + server.execute().getSubuserManager().createUser() + .setEmail(email) + .setPermissions(Permission.CONTROL_PERMISSIONS).execute(); } public void removeAccountFromServer(String id) { - JSONObject userData = getUserData(id); - if (userData == null) - { - FLog.severe("The Pterodactyl user with the ID of " + id + " was not found"); - return; - } - - String url = URL + "/api/client/servers/" + ConfigEntry.PTERO_SERVER_UUID.getString() + "/users/" + userData.get("uuid"); - - try - { - FUtil.sendRequest(url, "DELETE", SERVER_HEADERS, null); - } - catch (IOException e) - { - FLog.severe(e); - } + ApplicationUser username = adminAPI.retrieveUserById(id).execute(); + PteroAction server = clientAPI.retrieveServerByIdentifier(IDENTIFIER); + ClientSubuser clientSubuser = server.execute().getSubuser(username.getUUID()).retrieve().execute(); + server.execute().getSubuserManager().deleteUser(clientSubuser).execute(); } - public JSONObject getUserData(String id) + public void stopServer() { - Response response; - JSONObject jsonResponse; - try - { - response = FUtil.sendRequest(URL + "/api/application/users/" + id, "GET", ADMIN_HEADERS, null); - jsonResponse = response.getJSONMessage(); - - } - catch (IOException | ParseException e) - { - FLog.severe(e); - return null; - } - - return (JSONObject)jsonResponse.get("attributes"); - + clientAPI.setPower(server, PowerAction.STOP).execute(); } - // API patch function on users doesnt work rn, it throws 500 errors, so it's probably not written yet - @SuppressWarnings("unchecked") - public void setPassword(String id, String password) + public void restartServer() { - JSONObject json = new JSONObject(); - json.put("password", password); - - try - { - FUtil.sendRequest(URL + "/api/application/users/" + id, "PATCH", ADMIN_HEADERS, json.toJSONString()); - } - catch (IOException e) - { - FLog.severe(e); - } - } - - public String getURL() - { - return URL; - } - - public String getServerKey() - { - return SERVER_KEY; - } - - public String getAdminKey() - { - return ADMIN_KEY; - } - - public List getServerHeaders() - { - return SERVER_HEADERS; - } - - public List getAdminHeaders() - { - return ADMIN_HEADERS; + clientAPI.setPower(server, PowerAction.RESTART).execute(); } public boolean isEnabled() diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_panel.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_panel.java index 364d58ff..37fdcc35 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_panel.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_panel.java @@ -17,11 +17,9 @@ import org.bukkit.entity.Player; @CommandParameters(description = "Manage your Pterodactyl panel account", usage = "/ ") public class Command_panel extends FreedomCommand { - @Override public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) { - if (!plugin.ptero.isEnabled()) { msg("Pterodactyl integration is currently disabled.", ChatColor.RED); @@ -32,7 +30,7 @@ public class Command_panel extends FreedomCommand if (playerData.getDiscordID() == null) { - msg("You must have a linked discord account.", ChatColor.RED); + msg("You must have a linked Discord account.", ChatColor.RED); return true; } @@ -63,13 +61,11 @@ public class Command_panel extends FreedomCommand } plugin.ptero.addAccountToServer(id); - admin.setPteroID(id); plugin.al.save(admin); plugin.al.updateTables(); - plugin.dc.sendPteroInfo(playerData, username, password); - msg("Successfully created your Pterodactyl account. Check your DMs from " + plugin.dc.formatBotTag() + " on discord to get your credentials.", ChatColor.GREEN); + msg("Successfully created your Pterodactyl account. Check your DMs from " + plugin.dc.formatBotTag() + " on Discord to get your credentials.", ChatColor.GREEN); return true; } else if (args[0].equalsIgnoreCase("delete")) @@ -83,18 +79,10 @@ public class Command_panel extends FreedomCommand return true; } - boolean deleted = plugin.ptero.deleteAccount(admin.getPteroID()); - - if (!deleted) - { - msg("Failed to delete your Pterodactyl account.", ChatColor.RED); - return true; - } - + plugin.ptero.deleteAccount(admin.getPteroID()); admin.setPteroID(null); plugin.al.save(admin); plugin.al.updateTables(); - msg("Successfully deleted your Pterodactyl account.", ChatColor.GREEN); return true; } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_restart.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_restart.java new file mode 100644 index 00000000..49117459 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_restart.java @@ -0,0 +1,77 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Restart the server", usage = "/ [reason]") +public class Command_restart extends FreedomCommand +{ + private static final Map RESTART_CONFIRM = new HashMap<>(); + + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (!plugin.ptero.isEnabled()) + { + msg("Pterodactyl integration is currently disabled.", ChatColor.RED); + return true; + } + + String reason = "Server is restarting!"; + + if (args.length != 0) + { + reason = StringUtils.join(args, " "); + } + + if (sender.getName().equals("CONSOLE")) + { + restart(reason); + return true; + } + else if (RESTART_CONFIRM.containsKey(sender)) + { + restart(RESTART_CONFIRM.get(sender)); + return true; + } + + msg("Warning: You're about to restart the server. Type /restart again to confirm you want to do this."); + + RESTART_CONFIRM.put(sender, reason); + new BukkitRunnable() + { + @Override + public void run() + { + if (RESTART_CONFIRM.containsKey(sender)) + { + RESTART_CONFIRM.remove(sender); + msg("Restart request expired."); + } + } + }.runTaskLater(plugin, 15 * 20); + return true; + } + + public void restart(String reason) + { + FUtil.bcastMsg("Server is restarting!", ChatColor.LIGHT_PURPLE); + + for (Player player : server.getOnlinePlayers()) + { + player.kickPlayer(ChatColor.LIGHT_PURPLE + reason); + } + + RESTART_CONFIRM.remove(sender); + plugin.ptero.restartServer(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stop.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stop.java index c160c59f..21e862d5 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stop.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stop.java @@ -20,7 +20,6 @@ public class Command_stop extends FreedomCommand @Override public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) { - String reason = "Server is going offline, come back in about 20 seconds."; if (args.length != 0) @@ -39,7 +38,6 @@ public class Command_stop extends FreedomCommand return true; } - msg("Warning: You're about to stop the server. Type /stop again to confirm you want to do this."); STOP_CONFIRM.put(sender, reason); @@ -69,6 +67,13 @@ public class Command_stop extends FreedomCommand STOP_CONFIRM.remove(sender); - server.shutdown(); + if (plugin.ptero.isEnabled()) + { + plugin.ptero.stopServer(); + } + else + { + server.shutdown(); + } } } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java index 05051905..2a20d747 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java @@ -91,7 +91,7 @@ public enum ConfigEntry PTERO_DEFAULT_EMAIL_DOMAIN(String.class, "ptero.default_email_domain"), PTERO_SERVER_UUID(String.class, "ptero.server_uuid"), PTERO_ADMIN_KEY(String.class, "ptero.admin_key"), - PTERO_SERVER_KEY(String.class, "ptero.server_key"), + PTERO_CLIENT_KEY(String.class, "ptero.client_key"), // SHOP_ENABLED(Boolean.class, "shop.enabled"), SHOP_TITLE(String.class, "shop.title"), diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 3780220d..0a868335 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -85,18 +85,23 @@ discord: # Owner role ID server_owner_role_id: '' -# Pterodactyl +# Pterodactyl (requires Discord verification to be enabled) ptero: # URL - do not leave a trailing forward slash url: '' # The default email domain used to set email addresses for new users - do not include the @ default_email_domain: 'example.com' - # Server UUID + # Short Server UUID (for example: a538a220) + # Do NOT give the long Server UUID server_uuid: '' # Admin panel API key + # Create this key by going to Settings > Application API > Create New + # Make sure you give it Users and Servers permissions admin_key: '' - # Server API key - server_key: '' + # Client API key + # Create a new user and add them as a subuser to the server with all control privileges. + # Then, login to the new user account and go to User Settings. Click on API Credentials and create a new one. + client_key: '' # The shop shop: