mirror of
https://github.com/songoda/SongodaCore.git
synced 2025-01-10 01:38:31 +01:00
feat: Remove Craftaro's plugin license check
In a way, this can be considered an performance improvement
This commit is contained in:
parent
156aa628af
commit
9b9c8fb087
@ -7,10 +7,6 @@ import com.craftaro.core.core.PluginInfo;
|
||||
import com.craftaro.core.core.PluginInfoModule;
|
||||
import com.craftaro.core.core.SongodaCoreCommand;
|
||||
import com.craftaro.core.core.SongodaCoreDiagCommand;
|
||||
import com.craftaro.core.core.SongodaCoreLicenseCommand;
|
||||
import com.craftaro.core.core.SongodaCoreUUIDCommand;
|
||||
import com.craftaro.core.verification.CraftaroProductVerification;
|
||||
import com.craftaro.core.verification.ProductVerificationStatus;
|
||||
import com.cryptomorin.xseries.XMaterial;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -54,7 +50,7 @@ public class SongodaCore {
|
||||
* @deprecated The Core's version should be used instead as it uses Semantic Versioning
|
||||
*/
|
||||
@Deprecated
|
||||
private static final int coreRevision = 10;
|
||||
private static final int coreRevision = 11;
|
||||
|
||||
/**
|
||||
* @since coreRevision 6
|
||||
@ -193,7 +189,7 @@ public class SongodaCore {
|
||||
private void init() {
|
||||
this.shadingListener = new ShadedEventListener();
|
||||
this.commandManager.registerCommandDynamically(new SongodaCoreCommand())
|
||||
.addSubCommands(new SongodaCoreDiagCommand(), new SongodaCoreUUIDCommand(), new SongodaCoreLicenseCommand());
|
||||
.addSubCommands(new SongodaCoreDiagCommand());
|
||||
Bukkit.getPluginManager().registerEvents(this.loginListener, this.piggybackedPlugin);
|
||||
Bukkit.getPluginManager().registerEvents(this.shadingListener, this.piggybackedPlugin);
|
||||
|
||||
@ -231,17 +227,8 @@ public class SongodaCore {
|
||||
private ArrayList<BukkitTask> tasks = new ArrayList<>();
|
||||
|
||||
private void register(JavaPlugin plugin, int pluginID, String icon, String libraryVersion) {
|
||||
ProductVerificationStatus verificationStatus = ProductVerificationStatus.VERIFIED;
|
||||
if (pluginID > 0) {
|
||||
try {
|
||||
verificationStatus = CraftaroProductVerification.getProductVerificationStatus(pluginID);
|
||||
} catch (IOException ex) {
|
||||
getLogger().log(Level.WARNING, "Error verifying plugin " + plugin.getName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
getLogger().info(getPrefix() + "Hooked " + plugin.getName() + ".");
|
||||
PluginInfo info = new PluginInfo(plugin, pluginID, icon, libraryVersion, verificationStatus);
|
||||
PluginInfo info = new PluginInfo(plugin, pluginID, icon, libraryVersion);
|
||||
|
||||
// don't forget to check for language pack updates ;)
|
||||
info.addModule(new LocaleModule());
|
||||
|
@ -152,27 +152,6 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||
|
||||
CommandSender console = Bukkit.getConsoleSender();
|
||||
|
||||
// Check plugin access, don't load the plugin if the user doesn't have access
|
||||
if (CraftaroProductVerification.getOwnProductVerificationStatus() != ProductVerificationStatus.VERIFIED) {
|
||||
console.sendMessage("\n" +
|
||||
ChatColor.RED + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
|
||||
ChatColor.RED + "You do not have access to the " + getDescription().getName() + " plugin.\n" +
|
||||
ChatColor.YELLOW + "Please purchase a license at https://craftaro.com/\n" +
|
||||
ChatColor.YELLOW + "or set up your license\n" +
|
||||
ChatColor.YELLOW + "And setup it up:\n" +
|
||||
ChatColor.YELLOW + "Run the command " + ChatColor.GOLD + "/craftaro license" + ChatColor.YELLOW + " and follow the instructions\n" +
|
||||
ChatColor.RED + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
this.licensePreventedPluginLoad = true;
|
||||
SongodaCore.registerPlugin(this, CraftaroProductVerification.getProductId(), (XMaterial) null);
|
||||
|
||||
getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> {
|
||||
String pluginName = getDescription().getName();
|
||||
String pluginUrl = "https://craftaro.com/marketplace/product/" + CraftaroProductVerification.getProductId();
|
||||
Bukkit.broadcastMessage(ChatColor.RED + pluginName + " has not been activated. Please download " + pluginName + " here: " + pluginUrl);
|
||||
}, 5 * 20, 60 * 20);
|
||||
return;
|
||||
}
|
||||
|
||||
console.sendMessage(" "); // blank line to separate chatter
|
||||
console.sendMessage(ChatColor.GREEN + "=============================");
|
||||
console.sendMessage(String.format("%s%s %s by %sCraftaro <3!", ChatColor.GRAY, getDescription().getName(), getDescription().getVersion(), ChatColor.DARK_PURPLE));
|
||||
|
@ -2,7 +2,6 @@ package com.craftaro.core.core;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.core.dependency.DependencyLoader;
|
||||
import com.craftaro.core.verification.ProductVerificationStatus;
|
||||
import com.cryptomorin.xseries.XMaterial;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.json.simple.JSONObject;
|
||||
@ -17,7 +16,6 @@ public final class PluginInfo {
|
||||
protected final String coreIcon;
|
||||
protected final XMaterial icon;
|
||||
protected final String coreLibraryVersion;
|
||||
public final ProductVerificationStatus verificationStatus;
|
||||
|
||||
private final List<PluginInfoModule> modules = new ArrayList<>();
|
||||
private boolean hasUpdate = false;
|
||||
@ -27,13 +25,12 @@ public final class PluginInfo {
|
||||
private String marketplaceLink;
|
||||
private JSONObject json;
|
||||
|
||||
public PluginInfo(JavaPlugin javaPlugin, int songodaId, String icon, String coreLibraryVersion, ProductVerificationStatus verificationStatus) {
|
||||
public PluginInfo(JavaPlugin javaPlugin, int songodaId, String icon, String coreLibraryVersion) {
|
||||
this.javaPlugin = javaPlugin;
|
||||
this.songodaId = songodaId;
|
||||
this.coreIcon = icon;
|
||||
this.icon = CompatibleMaterial.getMaterial(icon).orElse(XMaterial.STONE);
|
||||
this.coreLibraryVersion = coreLibraryVersion;
|
||||
this.verificationStatus = verificationStatus;
|
||||
}
|
||||
|
||||
public String getLatestVersion() {
|
||||
|
@ -25,8 +25,6 @@ public class SongodaCoreCommand extends AbstractCommand {
|
||||
guiManager.showGUI((Player) sender, new SongodaCoreOverviewGUI());
|
||||
} else {
|
||||
sender.sendMessage("/craftaro diag");
|
||||
sender.sendMessage("/craftaro uuid");
|
||||
sender.sendMessage("/craftaro license");
|
||||
}
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
|
@ -1,101 +0,0 @@
|
||||
package com.craftaro.core.core;
|
||||
|
||||
import com.craftaro.core.commands.AbstractCommand;
|
||||
import com.craftaro.core.verification.AsyncTokenAcquisitionFlow;
|
||||
import com.craftaro.core.verification.CraftaroProductVerification;
|
||||
import com.craftaro.core.verification.ProductVerificationStatus;
|
||||
import com.craftaro.core.verification.VerificationRequest;
|
||||
import com.craftaro.core.SongodaCore;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SongodaCoreLicenseCommand extends AbstractCommand {
|
||||
|
||||
public SongodaCoreLicenseCommand() {
|
||||
super(CommandType.CONSOLE_OK, "license");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
try {
|
||||
boolean verificationNeedsAction = false;
|
||||
|
||||
if (SongodaCore.getPlugins().isEmpty()) {
|
||||
sender.sendMessage(SongodaCore.getPrefix() + ChatColor.RED + "No plugins found.");
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
sender.sendMessage("");
|
||||
for (PluginInfo pl : SongodaCore.getPlugins()) {
|
||||
if (pl.verificationStatus == ProductVerificationStatus.ACTION_NEEDED) {
|
||||
verificationNeedsAction = true;
|
||||
}
|
||||
|
||||
sender.sendMessage(String.format(
|
||||
ChatColor.YELLOW + "%s" + ChatColor.GRAY + ": %s",
|
||||
pl.getJavaPlugin().getName(),
|
||||
pl.verificationStatus.getColoredFriendlyName()
|
||||
));
|
||||
}
|
||||
sender.sendMessage("");
|
||||
|
||||
if (verificationNeedsAction) {
|
||||
AsyncTokenAcquisitionFlow tokenAcquisitionFlow = CraftaroProductVerification.startAsyncTokenAcquisitionFlow();
|
||||
sender.sendMessage(String.format(
|
||||
SongodaCore.getPrefix() + ChatColor.YELLOW + "Please visit " + ChatColor.GOLD + "%s " + ChatColor.YELLOW + "to verify your server.\nI'll be checking again every " + ChatColor.GOLD + "%d seconds " + ChatColor.GRAY + "–" + ChatColor.YELLOW + " You got about " + ChatColor.GOLD + "%d minutes " + ChatColor.YELLOW + "for this.",
|
||||
tokenAcquisitionFlow.getUriForTheUserToVisit(),
|
||||
TimeUnit.MILLISECONDS.toSeconds(VerificationRequest.CHECK_INTERVAL_MILLIS),
|
||||
TimeUnit.MILLISECONDS.toMinutes(VerificationRequest.REQUEST_TTL_MILLIS)
|
||||
));
|
||||
|
||||
tokenAcquisitionFlow.getResultFuture()
|
||||
.whenComplete((successful, throwable) -> {
|
||||
if (throwable != null) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, "Failed to acquire token", throwable);
|
||||
sender.sendMessage(SongodaCore.getPrefix() + ChatColor.RED + "The process failed " + ChatColor.GRAY + "– " + ChatColor.DARK_RED + "Please check the server log for details and try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!successful) {
|
||||
sender.sendMessage(SongodaCore.getPrefix() + ChatColor.RED + "The process failed " + ChatColor.GRAY + "–" + ChatColor.DARK_RED + " Please check the server log for details and try again later.");
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(SongodaCore.getPrefix() + ChatColor.GREEN + "The verification process has been completed " + ChatColor.GRAY + "–" + ChatColor.DARK_GREEN + " Please restart your server to finalize the process.");
|
||||
});
|
||||
|
||||
sender.sendMessage("");
|
||||
}
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
} catch (IOException ex) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, "Failed to check product verification status", ex);
|
||||
return ReturnType.FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "songoda.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/craftaro license";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Returns your server's uuid";
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package com.craftaro.core.core;
|
||||
|
||||
import com.craftaro.core.commands.AbstractCommand;
|
||||
import com.craftaro.core.utils.SongodaAuth;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SongodaCoreUUIDCommand extends AbstractCommand {
|
||||
|
||||
public SongodaCoreUUIDCommand() {
|
||||
super(CommandType.CONSOLE_OK, "uuid");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
sender.sendMessage("");
|
||||
if (sender instanceof Player) {
|
||||
TextComponent component = new TextComponent("Your server UUID is: " + SongodaAuth.getUUID());
|
||||
component.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, SongodaAuth.getUUID().toString()));
|
||||
component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new BaseComponent[]{new TextComponent("Click to copy")}));
|
||||
sender.spigot().sendMessage(component);
|
||||
} else {
|
||||
sender.sendMessage("Your server UUID is: " + SongodaAuth.getUUID());
|
||||
}
|
||||
sender.sendMessage("");
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "songoda.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/craftaro uuid";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Returns your server's uuid";
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
package com.craftaro.core.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
|
||||
@Deprecated
|
||||
public class SongodaAuth {
|
||||
@Deprecated
|
||||
public static boolean isAuthorized(boolean allowOffline) {
|
||||
String productId = "%%__PLUGIN__%%";
|
||||
if (isPluginSelfCompiled(productId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID serverUuid = getUUID();
|
||||
try {
|
||||
URL url = new URL("https://marketplace.songoda.com/api/v2/products/license/validate");
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
con.setRequestProperty("Content-Type", "application/json");
|
||||
con.setRequestProperty("Accept", "application/json");
|
||||
con.setDoOutput(true);
|
||||
|
||||
String requestBodyJson = "{\"product_id\":" + productId + ",\"license\":\"" + serverUuid + "\",\"user_id\":\"%%__USER__%%\"}";
|
||||
try (OutputStream os = con.getOutputStream()) {
|
||||
byte[] requestBody = requestBodyJson.getBytes(StandardCharsets.UTF_8);
|
||||
os.write(requestBody, 0, requestBody.length);
|
||||
}
|
||||
|
||||
JSONObject jsonResponse = readHttpResponseJson(con);
|
||||
if (jsonResponse.containsKey("error")) {
|
||||
Bukkit.getLogger().warning("Error validating license: " + jsonResponse.get("error"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return (boolean) jsonResponse.get("valid");
|
||||
} catch (Exception ex) {
|
||||
return allowOffline;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static UUID getUUID() {
|
||||
File serverProperties = new File("./server.properties");
|
||||
try {
|
||||
Properties prop = new Properties();
|
||||
prop.load(new FileReader(serverProperties));
|
||||
|
||||
String uuid = prop.getProperty("uuid");
|
||||
if (uuid != null && !uuid.isEmpty()) {
|
||||
return UUID.fromString(uuid);
|
||||
}
|
||||
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
prop.setProperty("uuid", newUUID.toString());
|
||||
prop.store(new FileWriter(serverProperties), null);
|
||||
return newUUID;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Could not determine UUID for server", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String getIP() {
|
||||
try {
|
||||
URL url = new URL("https://marketplace.songoda.com/api/v2/products/license/ip");
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("GET");
|
||||
con.setRequestProperty("Accept", "application/json");
|
||||
|
||||
JSONObject jsonResponse = readHttpResponseJson(con);
|
||||
return jsonResponse.get("ip").toString();
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Could not fetch IP address", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static JSONObject readHttpResponseJson(HttpURLConnection con) throws IOException, ParseException {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
StringBuilder response = new StringBuilder();
|
||||
|
||||
String responseLine;
|
||||
while ((responseLine = br.readLine()) != null) {
|
||||
response.append(responseLine.trim());
|
||||
}
|
||||
|
||||
return (JSONObject) new JSONParser().parse(response.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPluginSelfCompiled(String productId) {
|
||||
try {
|
||||
Integer.parseInt(productId);
|
||||
return false;
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.craftaro.core.verification;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class AsyncTokenAcquisitionFlow {
|
||||
private final String uri;
|
||||
private final CompletableFuture<Boolean> verificationResult;
|
||||
|
||||
public AsyncTokenAcquisitionFlow(String uri, CompletableFuture<Boolean> verificationResult) {
|
||||
this.uri = uri;
|
||||
this.verificationResult = verificationResult;
|
||||
}
|
||||
|
||||
public String getUriForTheUserToVisit() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> getResultFuture() {
|
||||
return this.verificationResult;
|
||||
}
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
package com.craftaro.core.verification;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.craftaro.core.SongodaCore;
|
||||
import com.craftaro.core.http.HttpClient;
|
||||
import com.craftaro.core.http.HttpResponse;
|
||||
import com.craftaro.core.http.SimpleHttpClient;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class CraftaroProductVerification {
|
||||
private static final String TOKEN_URI = "https://craftaro.com/api/v1/verification/token";
|
||||
private static final String TOKEN_REFRESH_URI = "https://craftaro.com/api/v1/verification/refresh";
|
||||
private static final String VERIFICATION_START_URI = "https://craftaro.com/api/v1/verification/uri";
|
||||
static final String VERIFICATION_STATUS_URI = "https://craftaro.com/api/v1/verification/state?request_id=%s";
|
||||
private static final String PRODUCT_ACCESS_URI = "https://craftaro.com/api/v1/verification/access?product_id=%d";
|
||||
|
||||
private static final HttpClient httpClient = new SimpleHttpClient();
|
||||
|
||||
private static @Nullable VerificationRequest verificationRequest;
|
||||
|
||||
public static ProductVerificationStatus getOwnProductVerificationStatus() {
|
||||
final int productId = getProductId();
|
||||
if (productId <= 0) {
|
||||
return ProductVerificationStatus.VERIFIED;
|
||||
}
|
||||
|
||||
try {
|
||||
return getProductVerificationStatus(productId);
|
||||
} catch (IOException ex) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, "Failed to fetch product verification status", ex);
|
||||
return ProductVerificationStatus.VERIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
public static ProductVerificationStatus getProductVerificationStatus(int productId) throws IOException {
|
||||
VerificationToken token = tryLoadExistingToken();
|
||||
if (tokenNeedsRefresh(token)) {
|
||||
if (token != null) {
|
||||
try {
|
||||
token = refreshVerificationToken(token);
|
||||
} catch (IOException ex) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, "Failed to refresh verification token", ex);
|
||||
return ProductVerificationStatus.VERIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
if (token == null) {
|
||||
return ProductVerificationStatus.ACTION_NEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Authorization", "Bearer " + token.accessToken);
|
||||
headers.put("Accept", "application/json");
|
||||
|
||||
HttpResponse productAccessResponse = httpClient.post(String.format(PRODUCT_ACCESS_URI, productId), headers, null);
|
||||
if (productAccessResponse.getResponseCode() == 404) {
|
||||
SongodaCore.getLogger().warning("The product could not be found on Craftaro!");
|
||||
return ProductVerificationStatus.VERIFIED;
|
||||
}
|
||||
if (productAccessResponse.getResponseCode() != 200) {
|
||||
throw new IOException("Failed to check product access – Got {code=" + productAccessResponse.getResponseCode() + ",body=" + productAccessResponse.getBodyAsString() + "}");
|
||||
}
|
||||
|
||||
String productAccessResponseJson = productAccessResponse.getBodyAsString();
|
||||
JsonObject productAccess = JsonParser.parseString(productAccessResponseJson).getAsJsonObject();
|
||||
|
||||
if (productAccess.get("has_access").getAsBoolean()) {
|
||||
return ProductVerificationStatus.VERIFIED;
|
||||
} else {
|
||||
return ProductVerificationStatus.UNVERIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
public static AsyncTokenAcquisitionFlow startAsyncTokenAcquisitionFlow() throws IOException {
|
||||
tryDeleteTokenFile();
|
||||
|
||||
if (verificationRequest != null) {
|
||||
verificationRequest.cancel(true);
|
||||
verificationRequest = null;
|
||||
}
|
||||
|
||||
HttpResponse verificationStartResponse = httpClient.get(VERIFICATION_START_URI);
|
||||
if (verificationStartResponse.getResponseCode() != 200) {
|
||||
throw new IOException("Failed to start verification process – Got {code=" + verificationStartResponse.getResponseCode() + ",body=" + verificationStartResponse.getBodyAsString() + "}");
|
||||
}
|
||||
|
||||
String verificationStartResponseJson = verificationStartResponse.getBodyAsString();
|
||||
JsonObject verificationStart = JsonParser.parseString(verificationStartResponseJson).getAsJsonObject();
|
||||
|
||||
String uri = verificationStart.get("uri").getAsString();
|
||||
String requestId = verificationStart.get("request_id").getAsString();
|
||||
String code = verificationStart.get("code").getAsString();
|
||||
|
||||
CompletableFuture<Boolean> asyncTokenRefreshWorkflowFuture = new CompletableFuture<>();
|
||||
|
||||
verificationRequest = new VerificationRequest(httpClient, requestId);
|
||||
verificationRequest
|
||||
.whenComplete((status, ex) -> {
|
||||
if (ex != null) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, SongodaCore.getPrefix() + "Failed to complete verification request", ex);
|
||||
asyncTokenRefreshWorkflowFuture.completeExceptionally(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status != VerificationRequest.Status.APPROVED) {
|
||||
SongodaCore.getLogger().warning(SongodaCore.getPrefix() + "Craftaro Product Verification request was denied!");
|
||||
asyncTokenRefreshWorkflowFuture.complete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Content-Type", "application/json");
|
||||
headers.put("Accept", "application/json");
|
||||
|
||||
JsonObject reqBody = new JsonObject();
|
||||
reqBody.addProperty("request_id", requestId);
|
||||
reqBody.addProperty("code", code);
|
||||
|
||||
HttpResponse tokenResponse = httpClient.post(TOKEN_URI, headers, reqBody.toString().getBytes(StandardCharsets.UTF_8));
|
||||
if (tokenResponse.getResponseCode() != 200) {
|
||||
throw new IOException("Failed to get verification token – Got {code=" + tokenResponse.getResponseCode() + ",body=" + tokenResponse.getBodyAsString() + "}");
|
||||
}
|
||||
|
||||
VerificationToken token = VerificationToken.fromJson(tokenResponse.getBodyAsString());
|
||||
VerificationTokenFileManager.saveVerificationToken(token);
|
||||
|
||||
SongodaCore.getLogger().info(SongodaCore.getPrefix() + "Craftaro Product Verification request was approved!");
|
||||
SongodaCore.getLogger().info(SongodaCore.getPrefix() + "Please restart your server to complete the verification process.");
|
||||
asyncTokenRefreshWorkflowFuture.complete(true);
|
||||
} catch (IOException ioException) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, SongodaCore.getPrefix() + "Failed to save verification token file", ioException);
|
||||
asyncTokenRefreshWorkflowFuture.completeExceptionally(ioException);
|
||||
}
|
||||
});
|
||||
|
||||
return new AsyncTokenAcquisitionFlow(uri, asyncTokenRefreshWorkflowFuture);
|
||||
}
|
||||
|
||||
public static int getProductId() {
|
||||
final String productId = "%%__PRODUCT_ID__%%";
|
||||
if (!productId.matches("[0-9]+")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Integer.parseInt(productId);
|
||||
}
|
||||
|
||||
private static @Nullable VerificationToken refreshVerificationToken(VerificationToken token) throws IOException {
|
||||
JsonObject reqBody = new JsonObject();
|
||||
reqBody.addProperty("access_token", token.accessToken);
|
||||
reqBody.addProperty("refresh_token", token.refreshToken);
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Content-Type", "application/json");
|
||||
headers.put("Accept", "application/json");
|
||||
|
||||
HttpResponse response = httpClient.post(TOKEN_REFRESH_URI, headers, reqBody.toString().getBytes(StandardCharsets.UTF_8));
|
||||
int statusCode = response.getResponseCode();
|
||||
|
||||
if (statusCode >= 400 && statusCode < 500) {
|
||||
return null;
|
||||
}
|
||||
if (statusCode != 200) {
|
||||
throw new IOException("Failed to refresh verification token – Got {code=" + statusCode + ",body=" + response.getBodyAsString() + "}");
|
||||
}
|
||||
|
||||
return VerificationToken.fromJson(response.getBodyAsString());
|
||||
}
|
||||
|
||||
private static @Nullable VerificationToken tryLoadExistingToken() {
|
||||
try {
|
||||
return VerificationTokenFileManager.loadVerificationToken();
|
||||
} catch (IOException ex) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, "Failed to load verification token file", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void tryDeleteTokenFile() {
|
||||
try {
|
||||
VerificationTokenFileManager.deleteVerificationTokenFile();
|
||||
} catch (IOException ex) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, "Failed to delete verification token file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tokenNeedsRefresh(@Nullable VerificationToken token) {
|
||||
if (token == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return System.currentTimeMillis() >= (token.expiresAt - TimeUnit.DAYS.toSeconds(3));
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package com.craftaro.core.verification;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
public enum ProductVerificationStatus {
|
||||
VERIFIED("Verified", ChatColor.GREEN),
|
||||
UNVERIFIED("Unverified", ChatColor.RED),
|
||||
ACTION_NEEDED("Verification needed", ChatColor.YELLOW);
|
||||
|
||||
private final String friendlyName;
|
||||
private final ChatColor chatColor;
|
||||
|
||||
ProductVerificationStatus(String friendlyName, ChatColor chatColor) {
|
||||
this.friendlyName = friendlyName;
|
||||
this.chatColor = chatColor;
|
||||
}
|
||||
|
||||
public String getFriendlyName() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
public String getColoredFriendlyName() {
|
||||
return this.chatColor + this.friendlyName;
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
package com.craftaro.core.verification;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import com.craftaro.core.SongodaCore;
|
||||
import com.craftaro.core.http.HttpClient;
|
||||
import com.craftaro.core.http.HttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class VerificationRequest extends CompletableFuture<VerificationRequest.Status> {
|
||||
public static final long REQUEST_TTL_MILLIS = TimeUnit.MINUTES.toMillis(15);
|
||||
public static final long CHECK_INTERVAL_MILLIS = TimeUnit.SECONDS.toMillis(10);
|
||||
|
||||
private final long requestExpiresAt = System.currentTimeMillis() + REQUEST_TTL_MILLIS;
|
||||
private final Timer taskTimer;
|
||||
|
||||
VerificationRequest(HttpClient httpClient, String requestId) {
|
||||
this.taskTimer = new Timer(true);
|
||||
this.taskTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (System.currentTimeMillis() > VerificationRequest.this.requestExpiresAt) {
|
||||
fulfill(Status.DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String urlEncodedRequestId = URLEncoder.encode(requestId, "UTF-8");
|
||||
HttpResponse verificationStatusResponse = httpClient.get(String.format(CraftaroProductVerification.VERIFICATION_STATUS_URI, urlEncodedRequestId));
|
||||
if (verificationStatusResponse.getResponseCode() != 200) {
|
||||
throw new IOException("Failed to check verification status – Got Status-Code " + verificationStatusResponse.getResponseCode());
|
||||
}
|
||||
|
||||
String verificationStatus = verificationStatusResponse.getBodyAsString();
|
||||
String verificationState = JsonParser.parseString(verificationStatus).getAsJsonObject().get("state").getAsString();
|
||||
|
||||
if (verificationState == null) {
|
||||
SongodaCore.getLogger().warning(SongodaCore.getPrefix() + "The Craftaro verification process timed out");
|
||||
fulfill(Status.DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
Status status = Status.fromResponseValue(verificationState);
|
||||
if (status != null && status != Status.PENDING) {
|
||||
fulfill(status);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
SongodaCore.getLogger().log(Level.WARNING, SongodaCore.getPrefix() + "Failed to check verification status", ex);
|
||||
}
|
||||
}
|
||||
}, CHECK_INTERVAL_MILLIS, CHECK_INTERVAL_MILLIS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
this.taskTimer.cancel();
|
||||
return super.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
private void fulfill(Status status) {
|
||||
this.taskTimer.cancel();
|
||||
super.complete(status);
|
||||
}
|
||||
|
||||
enum Status {
|
||||
PENDING("pending"),
|
||||
APPROVED("approved"),
|
||||
DENIED("denied");
|
||||
|
||||
final String responseValue;
|
||||
|
||||
Status(String responseValue) {
|
||||
this.responseValue = responseValue;
|
||||
}
|
||||
|
||||
static Status fromResponseValue(String responseValue) {
|
||||
for (Status status : values()) {
|
||||
if (status.responseValue.equalsIgnoreCase(responseValue)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package com.craftaro.core.verification;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
final class VerificationToken {
|
||||
public final String accessToken;
|
||||
public final String refreshToken;
|
||||
public final long expiresAt;
|
||||
|
||||
VerificationToken(String accessToken, String refreshToken, long expiresAt) {
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
this.expiresAt = expiresAt;
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("access_token", this.accessToken);
|
||||
json.addProperty("refresh_token", this.refreshToken);
|
||||
json.addProperty("expiresAt", this.expiresAt);
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
static VerificationToken fromJson(String json) {
|
||||
JsonObject jsonObj = (JsonObject) JsonParser.parseString(json);
|
||||
|
||||
long expiresAt;
|
||||
if (jsonObj.has("expiresAt")) {
|
||||
expiresAt = jsonObj.get("expiresAt").getAsLong();
|
||||
} else {
|
||||
expiresAt = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(jsonObj.get("expires_in").getAsInt());
|
||||
}
|
||||
|
||||
return new VerificationToken(
|
||||
jsonObj.get("access_token").getAsString(),
|
||||
jsonObj.get("refresh_token").getAsString(),
|
||||
expiresAt
|
||||
);
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package com.craftaro.core.verification;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Base64;
|
||||
|
||||
final class VerificationTokenFileManager {
|
||||
static VerificationToken loadVerificationToken() throws IOException {
|
||||
File verificationTokenFile = getVerificationTokenFile();
|
||||
if (!verificationTokenFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String base64TokenString = new String(Files.readAllBytes(verificationTokenFile.toPath()), StandardCharsets.UTF_8);
|
||||
String jsonTokenString = new String(Base64.getDecoder().decode(base64TokenString), StandardCharsets.UTF_8);
|
||||
return VerificationToken.fromJson(jsonTokenString);
|
||||
}
|
||||
|
||||
static void saveVerificationToken(VerificationToken token) throws IOException {
|
||||
File verificationTokenFile = getVerificationTokenFile();
|
||||
Files.createDirectories(verificationTokenFile.getParentFile().toPath());
|
||||
|
||||
String base64TokenString = Base64.getEncoder().encodeToString(token.toJson().getBytes(StandardCharsets.UTF_8));
|
||||
Files.write(verificationTokenFile.toPath(), base64TokenString.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
static void deleteVerificationTokenFile() throws IOException {
|
||||
File verificationTokenFile = getVerificationTokenFile();
|
||||
Files.deleteIfExists(verificationTokenFile.toPath());
|
||||
}
|
||||
|
||||
private static File getVerificationTokenFile() {
|
||||
return new File(getCraftaroDirectory(), "verification");
|
||||
}
|
||||
|
||||
private static File getCraftaroDirectory() {
|
||||
File pluginsDirectory = Bukkit.getPluginManager().getPlugins()[0].getDataFolder().getParentFile();
|
||||
return new File(pluginsDirectory, "Craftaro");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user