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: