feat: Remove Craftaro's plugin license check

In a way, this can be considered an performance improvement
This commit is contained in:
Christian Koop 2024-03-26 20:53:08 +01:00
parent 156aa628af
commit 9b9c8fb087
No known key found for this signature in database
GPG Key ID: 89A8181384E010A3
13 changed files with 4 additions and 742 deletions

View File

@ -7,10 +7,6 @@ import com.craftaro.core.core.PluginInfo;
import com.craftaro.core.core.PluginInfoModule; import com.craftaro.core.core.PluginInfoModule;
import com.craftaro.core.core.SongodaCoreCommand; import com.craftaro.core.core.SongodaCoreCommand;
import com.craftaro.core.core.SongodaCoreDiagCommand; 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 com.cryptomorin.xseries.XMaterial;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; 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 The Core's version should be used instead as it uses Semantic Versioning
*/ */
@Deprecated @Deprecated
private static final int coreRevision = 10; private static final int coreRevision = 11;
/** /**
* @since coreRevision 6 * @since coreRevision 6
@ -193,7 +189,7 @@ public class SongodaCore {
private void init() { private void init() {
this.shadingListener = new ShadedEventListener(); this.shadingListener = new ShadedEventListener();
this.commandManager.registerCommandDynamically(new SongodaCoreCommand()) 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.loginListener, this.piggybackedPlugin);
Bukkit.getPluginManager().registerEvents(this.shadingListener, this.piggybackedPlugin); Bukkit.getPluginManager().registerEvents(this.shadingListener, this.piggybackedPlugin);
@ -231,17 +227,8 @@ public class SongodaCore {
private ArrayList<BukkitTask> tasks = new ArrayList<>(); private ArrayList<BukkitTask> tasks = new ArrayList<>();
private void register(JavaPlugin plugin, int pluginID, String icon, String libraryVersion) { 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() + "."); 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 ;) // don't forget to check for language pack updates ;)
info.addModule(new LocaleModule()); info.addModule(new LocaleModule());

View File

@ -152,27 +152,6 @@ public abstract class SongodaPlugin extends JavaPlugin {
CommandSender console = Bukkit.getConsoleSender(); 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(" "); // blank line to separate chatter
console.sendMessage(ChatColor.GREEN + "============================="); console.sendMessage(ChatColor.GREEN + "=============================");
console.sendMessage(String.format("%s%s %s by %sCraftaro <3!", ChatColor.GRAY, getDescription().getName(), getDescription().getVersion(), ChatColor.DARK_PURPLE)); console.sendMessage(String.format("%s%s %s by %sCraftaro <3!", ChatColor.GRAY, getDescription().getName(), getDescription().getVersion(), ChatColor.DARK_PURPLE));

View File

@ -2,7 +2,6 @@ package com.craftaro.core.core;
import com.craftaro.core.compatibility.CompatibleMaterial; import com.craftaro.core.compatibility.CompatibleMaterial;
import com.craftaro.core.dependency.DependencyLoader; import com.craftaro.core.dependency.DependencyLoader;
import com.craftaro.core.verification.ProductVerificationStatus;
import com.cryptomorin.xseries.XMaterial; import com.cryptomorin.xseries.XMaterial;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
@ -17,7 +16,6 @@ public final class PluginInfo {
protected final String coreIcon; protected final String coreIcon;
protected final XMaterial icon; protected final XMaterial icon;
protected final String coreLibraryVersion; protected final String coreLibraryVersion;
public final ProductVerificationStatus verificationStatus;
private final List<PluginInfoModule> modules = new ArrayList<>(); private final List<PluginInfoModule> modules = new ArrayList<>();
private boolean hasUpdate = false; private boolean hasUpdate = false;
@ -27,13 +25,12 @@ public final class PluginInfo {
private String marketplaceLink; private String marketplaceLink;
private JSONObject json; 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.javaPlugin = javaPlugin;
this.songodaId = songodaId; this.songodaId = songodaId;
this.coreIcon = icon; this.coreIcon = icon;
this.icon = CompatibleMaterial.getMaterial(icon).orElse(XMaterial.STONE); this.icon = CompatibleMaterial.getMaterial(icon).orElse(XMaterial.STONE);
this.coreLibraryVersion = coreLibraryVersion; this.coreLibraryVersion = coreLibraryVersion;
this.verificationStatus = verificationStatus;
} }
public String getLatestVersion() { public String getLatestVersion() {

View File

@ -25,8 +25,6 @@ public class SongodaCoreCommand extends AbstractCommand {
guiManager.showGUI((Player) sender, new SongodaCoreOverviewGUI()); guiManager.showGUI((Player) sender, new SongodaCoreOverviewGUI());
} else { } else {
sender.sendMessage("/craftaro diag"); sender.sendMessage("/craftaro diag");
sender.sendMessage("/craftaro uuid");
sender.sendMessage("/craftaro license");
} }
return ReturnType.SUCCESS; return ReturnType.SUCCESS;

View File

@ -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";
}
}

View File

@ -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";
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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
);
}
}

View File

@ -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");
}
}