mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-05-23 02:12:01 +00:00
Floating keys for pro users (#3535)
# Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
parent
b26ecbc3b7
commit
f94b8c3b22
@ -29,7 +29,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.46.1"
|
version = "0.46.2"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
// 17 is lowest but we support and recommend 21
|
// 17 is lowest but we support and recommend 21
|
||||||
|
@ -48,30 +48,46 @@ public class KeygenLicenseVerifier {
|
|||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
// Shared HTTP client for connection pooling
|
||||||
|
private static final HttpClient httpClient = HttpClient.newBuilder()
|
||||||
|
.version(HttpClient.Version.HTTP_2)
|
||||||
|
.connectTimeout(java.time.Duration.ofSeconds(10))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// License metadata context class to avoid shared mutable state
|
||||||
|
private static class LicenseContext {
|
||||||
|
private boolean isFloatingLicense = false;
|
||||||
|
private int maxMachines = 1; // Default to 1 if not specified
|
||||||
|
private boolean isEnterpriseLicense = false;
|
||||||
|
|
||||||
|
public LicenseContext() {}
|
||||||
|
}
|
||||||
|
|
||||||
public License verifyLicense(String licenseKeyOrCert) {
|
public License verifyLicense(String licenseKeyOrCert) {
|
||||||
License license;
|
License license;
|
||||||
|
LicenseContext context = new LicenseContext();
|
||||||
|
|
||||||
if (isCertificateLicense(licenseKeyOrCert)) {
|
if (isCertificateLicense(licenseKeyOrCert)) {
|
||||||
log.info("Detected certificate-based license. Processing...");
|
log.info("Detected certificate-based license. Processing...");
|
||||||
boolean isValid = verifyCertificateLicense(licenseKeyOrCert);
|
boolean isValid = verifyCertificateLicense(licenseKeyOrCert, context);
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
license = isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
|
license = context.isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
|
||||||
} else {
|
} else {
|
||||||
license = License.NORMAL;
|
license = License.NORMAL;
|
||||||
}
|
}
|
||||||
} else if (isJWTLicense(licenseKeyOrCert)) {
|
} else if (isJWTLicense(licenseKeyOrCert)) {
|
||||||
log.info("Detected JWT-style license key. Processing...");
|
log.info("Detected JWT-style license key. Processing...");
|
||||||
boolean isValid = verifyJWTLicense(licenseKeyOrCert);
|
boolean isValid = verifyJWTLicense(licenseKeyOrCert, context);
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
license = isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
|
license = context.isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
|
||||||
} else {
|
} else {
|
||||||
license = License.NORMAL;
|
license = License.NORMAL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info("Detected standard license key. Processing...");
|
log.info("Detected standard license key. Processing...");
|
||||||
boolean isValid = verifyStandardLicense(licenseKeyOrCert);
|
boolean isValid = verifyStandardLicense(licenseKeyOrCert, context);
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
license = isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
|
license = context.isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
|
||||||
} else {
|
} else {
|
||||||
license = License.NORMAL;
|
license = License.NORMAL;
|
||||||
}
|
}
|
||||||
@ -79,7 +95,7 @@ public class KeygenLicenseVerifier {
|
|||||||
return license;
|
return license;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEnterpriseLicense = false;
|
// Removed instance field for isEnterpriseLicense, now using LicenseContext
|
||||||
|
|
||||||
private boolean isCertificateLicense(String license) {
|
private boolean isCertificateLicense(String license) {
|
||||||
return license != null && license.trim().startsWith(CERT_PREFIX);
|
return license != null && license.trim().startsWith(CERT_PREFIX);
|
||||||
@ -89,7 +105,7 @@ public class KeygenLicenseVerifier {
|
|||||||
return license != null && license.trim().startsWith(JWT_PREFIX);
|
return license != null && license.trim().startsWith(JWT_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verifyCertificateLicense(String licenseFile) {
|
private boolean verifyCertificateLicense(String licenseFile, LicenseContext context) {
|
||||||
try {
|
try {
|
||||||
String encodedPayload = licenseFile;
|
String encodedPayload = licenseFile;
|
||||||
// Remove the header
|
// Remove the header
|
||||||
@ -144,7 +160,7 @@ public class KeygenLicenseVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the certificate data
|
// Process the certificate data
|
||||||
boolean isValid = processCertificateData(decodedData);
|
boolean isValid = processCertificateData(decodedData, context);
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -187,7 +203,7 @@ public class KeygenLicenseVerifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processCertificateData(String certData) {
|
private boolean processCertificateData(String certData, LicenseContext context) {
|
||||||
try {
|
try {
|
||||||
JSONObject licenseData = new JSONObject(certData);
|
JSONObject licenseData = new JSONObject(certData);
|
||||||
JSONObject metaObj = licenseData.optJSONObject("meta");
|
JSONObject metaObj = licenseData.optJSONObject("meta");
|
||||||
@ -229,15 +245,17 @@ public class KeygenLicenseVerifier {
|
|||||||
if (attributesObj != null) {
|
if (attributesObj != null) {
|
||||||
log.info("Found attributes in certificate data");
|
log.info("Found attributes in certificate data");
|
||||||
|
|
||||||
|
// Check for floating license
|
||||||
|
context.isFloatingLicense = attributesObj.optBoolean("floating", false);
|
||||||
|
context.maxMachines = attributesObj.optInt("maxMachines", 1);
|
||||||
|
|
||||||
// Extract metadata
|
// Extract metadata
|
||||||
JSONObject metadataObj = attributesObj.optJSONObject("metadata");
|
JSONObject metadataObj = attributesObj.optJSONObject("metadata");
|
||||||
if (metadataObj != null) {
|
if (metadataObj != null) {
|
||||||
int users = metadataObj.optInt("users", 0);
|
int users = metadataObj.optInt("users", 1);
|
||||||
if (users > 0) {
|
|
||||||
applicationProperties.getPremium().setMaxUsers(users);
|
applicationProperties.getPremium().setMaxUsers(users);
|
||||||
log.info("License allows for {} users", users);
|
log.info("License allows for {} users", users);
|
||||||
}
|
context.isEnterpriseLicense = metadataObj.optBoolean("isEnterprise", false);
|
||||||
isEnterpriseLicense = metadataObj.optBoolean("isEnterprise", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check license status if available
|
// Check license status if available
|
||||||
@ -257,7 +275,7 @@ public class KeygenLicenseVerifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verifyJWTLicense(String licenseKey) {
|
private boolean verifyJWTLicense(String licenseKey, LicenseContext context) {
|
||||||
try {
|
try {
|
||||||
log.info("Verifying ED25519_SIGN format license key");
|
log.info("Verifying ED25519_SIGN format license key");
|
||||||
|
|
||||||
@ -291,7 +309,7 @@ public class KeygenLicenseVerifier {
|
|||||||
String payload = new String(payloadBytes);
|
String payload = new String(payloadBytes);
|
||||||
|
|
||||||
// Process the license payload
|
// Process the license payload
|
||||||
boolean isValid = processJWTLicensePayload(payload);
|
boolean isValid = processJWTLicensePayload(payload, context);
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -327,7 +345,7 @@ public class KeygenLicenseVerifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processJWTLicensePayload(String payload) {
|
private boolean processJWTLicensePayload(String payload, LicenseContext context) {
|
||||||
try {
|
try {
|
||||||
log.info("Processing license payload: {}", payload);
|
log.info("Processing license payload: {}", payload);
|
||||||
|
|
||||||
@ -348,6 +366,13 @@ public class KeygenLicenseVerifier {
|
|||||||
String licenseId = licenseObj.optString("id", "unknown");
|
String licenseId = licenseObj.optString("id", "unknown");
|
||||||
log.info("Processing license with ID: {}", licenseId);
|
log.info("Processing license with ID: {}", licenseId);
|
||||||
|
|
||||||
|
// Check for floating license in license object
|
||||||
|
context.isFloatingLicense = licenseObj.optBoolean("floating", false);
|
||||||
|
context.maxMachines = licenseObj.optInt("maxMachines", 1);
|
||||||
|
if (context.isFloatingLicense) {
|
||||||
|
log.info("Detected floating license with max machines: {}", context.maxMachines);
|
||||||
|
}
|
||||||
|
|
||||||
// Check expiry date
|
// Check expiry date
|
||||||
String expiryStr = licenseObj.optString("expiry", null);
|
String expiryStr = licenseObj.optString("expiry", null);
|
||||||
if (expiryStr != null && !"null".equals(expiryStr)) {
|
if (expiryStr != null && !"null".equals(expiryStr)) {
|
||||||
@ -383,9 +408,20 @@ public class KeygenLicenseVerifier {
|
|||||||
String policyId = policyObj.optString("id", "unknown");
|
String policyId = policyObj.optString("id", "unknown");
|
||||||
log.info("License uses policy: {}", policyId);
|
log.info("License uses policy: {}", policyId);
|
||||||
|
|
||||||
|
// Check for floating license in policy
|
||||||
|
boolean policyFloating = policyObj.optBoolean("floating", false);
|
||||||
|
int policyMaxMachines = policyObj.optInt("maxMachines", 1);
|
||||||
|
|
||||||
|
// Policy settings take precedence
|
||||||
|
if (policyFloating) {
|
||||||
|
context.isFloatingLicense = true;
|
||||||
|
context.maxMachines = policyMaxMachines;
|
||||||
|
log.info("Policy defines floating license with max machines: {}", context.maxMachines);
|
||||||
|
}
|
||||||
|
|
||||||
// Extract max users and isEnterprise from policy or metadata
|
// Extract max users and isEnterprise from policy or metadata
|
||||||
int users = policyObj.optInt("users", 0);
|
int users = policyObj.optInt("users", 1);
|
||||||
isEnterpriseLicense = policyObj.optBoolean("isEnterprise", false);
|
context.isEnterpriseLicense = policyObj.optBoolean("isEnterprise", false);
|
||||||
|
|
||||||
if (users > 0) {
|
if (users > 0) {
|
||||||
applicationProperties.getPremium().setMaxUsers(users);
|
applicationProperties.getPremium().setMaxUsers(users);
|
||||||
@ -399,7 +435,7 @@ public class KeygenLicenseVerifier {
|
|||||||
log.info("License allows for {} users (from metadata)", users);
|
log.info("License allows for {} users (from metadata)", users);
|
||||||
|
|
||||||
// Check for isEnterprise flag in metadata
|
// Check for isEnterprise flag in metadata
|
||||||
isEnterpriseLicense = metadata.optBoolean("isEnterprise", false);
|
context.isEnterpriseLicense = metadata.optBoolean("isEnterprise", false);
|
||||||
} else {
|
} else {
|
||||||
// Default value
|
// Default value
|
||||||
applicationProperties.getPremium().setMaxUsers(1);
|
applicationProperties.getPremium().setMaxUsers(1);
|
||||||
@ -415,13 +451,13 @@ public class KeygenLicenseVerifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verifyStandardLicense(String licenseKey) {
|
private boolean verifyStandardLicense(String licenseKey, LicenseContext context) {
|
||||||
try {
|
try {
|
||||||
log.info("Checking standard license key");
|
log.info("Checking standard license key");
|
||||||
String machineFingerprint = generateMachineFingerprint();
|
String machineFingerprint = generateMachineFingerprint();
|
||||||
|
|
||||||
// First, try to validate the license
|
// First, try to validate the license
|
||||||
JsonNode validationResponse = validateLicense(licenseKey, machineFingerprint);
|
JsonNode validationResponse = validateLicense(licenseKey, machineFingerprint, context);
|
||||||
if (validationResponse != null) {
|
if (validationResponse != null) {
|
||||||
boolean isValid = validationResponse.path("meta").path("valid").asBoolean();
|
boolean isValid = validationResponse.path("meta").path("valid").asBoolean();
|
||||||
String licenseId = validationResponse.path("data").path("id").asText();
|
String licenseId = validationResponse.path("data").path("id").asText();
|
||||||
@ -435,10 +471,10 @@ public class KeygenLicenseVerifier {
|
|||||||
"License not activated for this machine. Attempting to"
|
"License not activated for this machine. Attempting to"
|
||||||
+ " activate...");
|
+ " activate...");
|
||||||
boolean activated =
|
boolean activated =
|
||||||
activateMachine(licenseKey, licenseId, machineFingerprint);
|
activateMachine(licenseKey, licenseId, machineFingerprint, context);
|
||||||
if (activated) {
|
if (activated) {
|
||||||
// Revalidate after activation
|
// Revalidate after activation
|
||||||
validationResponse = validateLicense(licenseKey, machineFingerprint);
|
validationResponse = validateLicense(licenseKey, machineFingerprint, context);
|
||||||
isValid =
|
isValid =
|
||||||
validationResponse != null
|
validationResponse != null
|
||||||
&& validationResponse
|
&& validationResponse
|
||||||
@ -458,9 +494,8 @@ public class KeygenLicenseVerifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonNode validateLicense(String licenseKey, String machineFingerprint)
|
private JsonNode validateLicense(String licenseKey, String machineFingerprint, LicenseContext context)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
HttpClient client = HttpClient.newHttpClient();
|
|
||||||
String requestBody =
|
String requestBody =
|
||||||
String.format(
|
String.format(
|
||||||
"{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\"}}}",
|
"{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\"}}}",
|
||||||
@ -479,7 +514,7 @@ public class KeygenLicenseVerifier {
|
|||||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
log.info("ValidateLicenseResponse body: {}", response.body());
|
log.info("ValidateLicenseResponse body: {}", response.body());
|
||||||
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
||||||
if (response.statusCode() == 200) {
|
if (response.statusCode() == 200) {
|
||||||
@ -493,18 +528,56 @@ public class KeygenLicenseVerifier {
|
|||||||
log.info("Validation detail: " + detail);
|
log.info("Validation detail: " + detail);
|
||||||
log.info("Validation code: " + code);
|
log.info("Validation code: " + code);
|
||||||
|
|
||||||
// Extract user count
|
// Check if the license itself has floating attribute
|
||||||
|
JsonNode licenseAttrs = jsonResponse.path("data").path("attributes");
|
||||||
|
if (!licenseAttrs.isMissingNode()) {
|
||||||
|
context.isFloatingLicense = licenseAttrs.path("floating").asBoolean(false);
|
||||||
|
context.maxMachines = licenseAttrs.path("maxMachines").asInt(1);
|
||||||
|
|
||||||
|
log.info("License floating (from license): {}, maxMachines: {}",
|
||||||
|
context.isFloatingLicense, context.maxMachines);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check the policy for floating license support if included
|
||||||
|
JsonNode includedNode = jsonResponse.path("included");
|
||||||
|
JsonNode policyNode = null;
|
||||||
|
|
||||||
|
if (includedNode.isArray()) {
|
||||||
|
for (JsonNode node : includedNode) {
|
||||||
|
if ("policies".equals(node.path("type").asText())) {
|
||||||
|
policyNode = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policyNode != null) {
|
||||||
|
// Check if this is a floating license from policy
|
||||||
|
boolean policyFloating = policyNode.path("attributes").path("floating").asBoolean(false);
|
||||||
|
int policyMaxMachines = policyNode.path("attributes").path("maxMachines").asInt(1);
|
||||||
|
|
||||||
|
// Policy takes precedence over license attributes
|
||||||
|
if (policyFloating) {
|
||||||
|
context.isFloatingLicense = true;
|
||||||
|
context.maxMachines = policyMaxMachines;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("License floating (from policy): {}, maxMachines: {}",
|
||||||
|
context.isFloatingLicense, context.maxMachines);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract user count, default to 1 if not specified
|
||||||
int users =
|
int users =
|
||||||
jsonResponse
|
jsonResponse
|
||||||
.path("data")
|
.path("data")
|
||||||
.path("attributes")
|
.path("attributes")
|
||||||
.path("metadata")
|
.path("metadata")
|
||||||
.path("users")
|
.path("users")
|
||||||
.asInt(0);
|
.asInt(1);
|
||||||
applicationProperties.getPremium().setMaxUsers(users);
|
applicationProperties.getPremium().setMaxUsers(users);
|
||||||
|
|
||||||
// Extract isEnterprise flag
|
// Extract isEnterprise flag
|
||||||
isEnterpriseLicense =
|
context.isEnterpriseLicense =
|
||||||
jsonResponse
|
jsonResponse
|
||||||
.path("data")
|
.path("data")
|
||||||
.path("attributes")
|
.path("attributes")
|
||||||
@ -520,10 +593,87 @@ public class KeygenLicenseVerifier {
|
|||||||
return jsonResponse;
|
return jsonResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean activateMachine(String licenseKey, String licenseId, String machineFingerprint)
|
private boolean activateMachine(String licenseKey, String licenseId, String machineFingerprint,
|
||||||
throws Exception {
|
LicenseContext context) throws Exception {
|
||||||
HttpClient client = HttpClient.newHttpClient();
|
// For floating licenses, we first need to check if we need to deregister any machines
|
||||||
|
if (context.isFloatingLicense) {
|
||||||
|
log.info("Processing floating license activation. Max machines allowed: {}", context.maxMachines);
|
||||||
|
|
||||||
|
// Get the current machines for this license
|
||||||
|
JsonNode machinesResponse = fetchMachinesForLicense(licenseKey, licenseId);
|
||||||
|
if (machinesResponse != null) {
|
||||||
|
JsonNode machines = machinesResponse.path("data");
|
||||||
|
int currentMachines = machines.size();
|
||||||
|
|
||||||
|
log.info("Current machine count: {}, Max allowed: {}", currentMachines, context.maxMachines);
|
||||||
|
|
||||||
|
// Check if the current fingerprint is already activated
|
||||||
|
boolean isCurrentMachineActivated = false;
|
||||||
|
String currentMachineId = null;
|
||||||
|
|
||||||
|
for (JsonNode machine : machines) {
|
||||||
|
if (machineFingerprint.equals(machine.path("attributes").path("fingerprint").asText())) {
|
||||||
|
isCurrentMachineActivated = true;
|
||||||
|
currentMachineId = machine.path("id").asText();
|
||||||
|
log.info("Current machine is already activated with ID: {}", currentMachineId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current machine is already activated, there's no need to do anything
|
||||||
|
if (isCurrentMachineActivated) {
|
||||||
|
log.info("Machine already activated. No action needed.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've reached the max machines limit, we need to deregister the oldest machine
|
||||||
|
if (currentMachines >= context.maxMachines) {
|
||||||
|
log.info("Max machines reached. Deregistering oldest machine to make room for the new machine.");
|
||||||
|
|
||||||
|
// Find the oldest machine based on creation timestamp
|
||||||
|
if (machines.size() > 0) {
|
||||||
|
// Find the machine with the oldest creation date
|
||||||
|
String oldestMachineId = null;
|
||||||
|
java.time.Instant oldestTime = null;
|
||||||
|
|
||||||
|
for (JsonNode machine : machines) {
|
||||||
|
String createdStr = machine.path("attributes").path("created").asText(null);
|
||||||
|
if (createdStr != null && !createdStr.isEmpty()) {
|
||||||
|
try {
|
||||||
|
java.time.Instant createdTime = java.time.Instant.parse(createdStr);
|
||||||
|
if (oldestTime == null || createdTime.isBefore(oldestTime)) {
|
||||||
|
oldestTime = createdTime;
|
||||||
|
oldestMachineId = machine.path("id").asText();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Could not parse creation time for machine: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we couldn't determine the oldest by timestamp, use the first one
|
||||||
|
if (oldestMachineId == null) {
|
||||||
|
log.warn("Could not determine oldest machine by timestamp, using first machine in list");
|
||||||
|
oldestMachineId = machines.path(0).path("id").asText();
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Deregistering machine with ID: {}", oldestMachineId);
|
||||||
|
|
||||||
|
boolean deregistered = deregisterMachine(licenseKey, oldestMachineId);
|
||||||
|
if (!deregistered) {
|
||||||
|
log.error("Failed to deregister machine. Cannot proceed with activation.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log.info("Machine deregistered successfully. Proceeding with activation of new machine.");
|
||||||
|
} else {
|
||||||
|
log.error("License has reached machine limit but no machines were found to deregister. This is unexpected.");
|
||||||
|
// We'll still try to activate, but it might fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with machine activation
|
||||||
String hostname;
|
String hostname;
|
||||||
try {
|
try {
|
||||||
hostname = java.net.InetAddress.getLocalHost().getHostName();
|
hostname = java.net.InetAddress.getLocalHost().getHostName();
|
||||||
@ -570,7 +720,7 @@ public class KeygenLicenseVerifier {
|
|||||||
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
|
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
log.info("activateMachine Response body: " + response.body());
|
log.info("activateMachine Response body: " + response.body());
|
||||||
if (response.statusCode() == 201) {
|
if (response.statusCode() == 201) {
|
||||||
log.info("Machine activated successfully");
|
log.info("Machine activated successfully");
|
||||||
@ -588,4 +738,66 @@ public class KeygenLicenseVerifier {
|
|||||||
private String generateMachineFingerprint() {
|
private String generateMachineFingerprint() {
|
||||||
return GeneralUtils.generateMachineFingerprint();
|
return GeneralUtils.generateMachineFingerprint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all machines associated with a specific license
|
||||||
|
*
|
||||||
|
* @param licenseKey The license key to check
|
||||||
|
* @param licenseId The license ID
|
||||||
|
* @return JsonNode containing the list of machines, or null if an error occurs
|
||||||
|
* @throws Exception if an error occurs during the HTTP request
|
||||||
|
*/
|
||||||
|
private JsonNode fetchMachinesForLicense(String licenseKey, String licenseId) throws Exception {
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/licenses/" + licenseId + "/machines"))
|
||||||
|
.header("Content-Type", "application/vnd.api+json")
|
||||||
|
.header("Accept", "application/vnd.api+json")
|
||||||
|
.header("Authorization", "License " + licenseKey)
|
||||||
|
.GET()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
log.info("fetchMachinesForLicense Response body: {}", response.body());
|
||||||
|
|
||||||
|
if (response.statusCode() == 200) {
|
||||||
|
return objectMapper.readTree(response.body());
|
||||||
|
} else {
|
||||||
|
log.error("Error fetching machines for license. Status code: {}, error: {}",
|
||||||
|
response.statusCode(), response.body());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deregisters a machine from a license
|
||||||
|
*
|
||||||
|
* @param licenseKey The license key
|
||||||
|
* @param machineId The ID of the machine to deregister
|
||||||
|
* @return true if deregistration was successful, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean deregisterMachine(String licenseKey, String machineId) {
|
||||||
|
try {
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines/" + machineId))
|
||||||
|
.header("Content-Type", "application/vnd.api+json")
|
||||||
|
.header("Accept", "application/vnd.api+json")
|
||||||
|
.header("Authorization", "License " + licenseKey)
|
||||||
|
.DELETE()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
|
if (response.statusCode() == 204) {
|
||||||
|
log.info("Machine {} successfully deregistered", machineId);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log.error("Error deregistering machine. Status code: {}, error: {}",
|
||||||
|
response.statusCode(), response.body());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Exception during machine deregistration: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user