mirror of
https://github.com/unraid/api.git
synced 2025-12-31 21:49:57 -06:00
feat: add ReplaceKey functionality to plugin (#1264)
This change enhances the plugin's capability to manage license keys effectively. - Introduced `ReplaceKey.php` from the webgui repo for handling auto-extended key check & installation - Updated dynamix.unraid.net.plg to include the new ReplaceKey.php in restore and preserve files. - Changed the `check()` method call in `Registration.page` to use the `force` parameter per https://app.asana.com/0/1204220153625175/1209573221367693/f - Moved the `require_once` for `reboot-details.php` in Downgrade.page and Update.page to ensure it's included after the `ReplaceKey` check. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced license key management now validates and updates credentials more reliably. - Essential configuration files are preserved throughout updates and uninstalls to maintain system integrity. - **Chores** - Optimized the update and registration workflows for a smoother, more stable user experience. - Adjusted internal processing steps to prepare for upcoming improvements in update checks. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -384,6 +384,7 @@ if [ -f /tmp/restore-files-dynamix-unraid-net ]; then
|
|||||||
"/usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php"
|
"/usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php"
|
||||||
"/usr/local/emhttp/plugins/dynamix/include/ProvisionCert.php"
|
"/usr/local/emhttp/plugins/dynamix/include/ProvisionCert.php"
|
||||||
"/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php"
|
"/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php"
|
||||||
|
"/usr/local/emhttp/plugins/dynamix/include/ReplaceKey.php"
|
||||||
"/usr/local/emhttp/plugins/dynamix/include/Wrappers.php"
|
"/usr/local/emhttp/plugins/dynamix/include/Wrappers.php"
|
||||||
"/usr/local/emhttp/plugins/dynamix.plugin.manager/Downgrade.page"
|
"/usr/local/emhttp/plugins/dynamix.plugin.manager/Downgrade.page"
|
||||||
"/usr/local/emhttp/plugins/dynamix.plugin.manager/Update.page"
|
"/usr/local/emhttp/plugins/dynamix.plugin.manager/Update.page"
|
||||||
@@ -505,6 +506,7 @@ echo
|
|||||||
preserveFilesDirs=(
|
preserveFilesDirs=(
|
||||||
"move:/usr/local/emhttp/plugins/dynamix/Registration.page:preventDowngrade"
|
"move:/usr/local/emhttp/plugins/dynamix/Registration.page:preventDowngrade"
|
||||||
"move:/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php:preventDowngrade"
|
"move:/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php:preventDowngrade"
|
||||||
|
"move:/usr/local/emhttp/plugins/dynamix/include/ReplaceKey.php:preventDowngrade"
|
||||||
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/Downgrade.page:preventDowngrade"
|
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/Downgrade.page:preventDowngrade"
|
||||||
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/Update.page:preventDowngrade"
|
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/Update.page:preventDowngrade"
|
||||||
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck:preventDowngrade"
|
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck:preventDowngrade"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Tag="pencil"
|
|||||||
*/
|
*/
|
||||||
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
||||||
$replaceKey = new ReplaceKey();
|
$replaceKey = new ReplaceKey();
|
||||||
$replaceKey->check();
|
$replaceKey->check(true);
|
||||||
?>
|
?>
|
||||||
<unraid-i18n-host>
|
<unraid-i18n-host>
|
||||||
<unraid-registration></unraid-registration>
|
<unraid-registration></unraid-registration>
|
||||||
|
|||||||
@@ -17,11 +17,10 @@ Tag="upload"
|
|||||||
* @note icon-update is rotated via CSS in myservers1.php
|
* @note icon-update is rotated via CSS in myservers1.php
|
||||||
*/
|
*/
|
||||||
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
||||||
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
|
|
||||||
|
|
||||||
$replaceKey = new ReplaceKey();
|
$replaceKey = new ReplaceKey();
|
||||||
$replaceKey->check();
|
$replaceKey->check();
|
||||||
|
|
||||||
|
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
|
||||||
$rebootDetails = new RebootDetails();
|
$rebootDetails = new RebootDetails();
|
||||||
$rebootDetails->setPrevious();
|
$rebootDetails->setPrevious();
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ Tag="upload"
|
|||||||
* The above copyright notice and this permission notice shall be included in
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* all copies or substantial portions of the Software.
|
* all copies or substantial portions of the Software.
|
||||||
*/
|
*/
|
||||||
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
|
|
||||||
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
||||||
$rebootDetails = new RebootDetails();
|
|
||||||
$replaceKey = new ReplaceKey();
|
$replaceKey = new ReplaceKey();
|
||||||
$replaceKey->check();
|
$replaceKey->check();
|
||||||
|
|
||||||
|
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
|
||||||
|
$rebootDetails = new RebootDetails();
|
||||||
?>
|
?>
|
||||||
<script>
|
<script>
|
||||||
function cleanUpFlashBackup(zip) {
|
function cleanUpFlashBackup(zip) {
|
||||||
|
|||||||
@@ -13,7 +13,11 @@
|
|||||||
?>
|
?>
|
||||||
<?
|
<?
|
||||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||||
require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php";
|
|
||||||
|
|
||||||
|
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
||||||
|
$replaceKey = new ReplaceKey();
|
||||||
|
$replaceKey->check();
|
||||||
|
|
||||||
|
require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php";
|
||||||
$unraidOsCheck = new UnraidOsCheck();
|
$unraidOsCheck = new UnraidOsCheck();
|
||||||
$unraidOsCheck->checkForUpdate();
|
$unraidOsCheck->checkForUpdate();
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
<?php
|
||||||
|
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||||
|
|
||||||
|
class ReplaceKey
|
||||||
|
{
|
||||||
|
private const KEY_SERVER_URL = 'https://keys.lime-technology.com';
|
||||||
|
|
||||||
|
private $docroot;
|
||||||
|
private $var;
|
||||||
|
private $guid;
|
||||||
|
private $keyfile;
|
||||||
|
private $regExp;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->docroot = $GLOBALS['docroot'] ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||||
|
|
||||||
|
$this->var = (array)@parse_ini_file('/var/local/emhttp/var.ini');
|
||||||
|
$this->guid = @$this->var['regGUID'] ?? null;
|
||||||
|
|
||||||
|
$keyfileBase64 = empty($this->var['regFILE']) ? null : @file_get_contents($this->var['regFILE']);
|
||||||
|
if ($keyfileBase64 !== false) {
|
||||||
|
$keyfileBase64 = @base64_encode($keyfileBase64);
|
||||||
|
$this->keyfile = str_replace(['+', '/', '='], ['-', '_', ''], trim($keyfileBase64));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->regExp = @$this->var['regExp'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function request($url, $method, $payload = null, $headers = null)
|
||||||
|
{
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set the request method
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||||
|
// store the response in a variable instead of printing it
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
// Set the payload if present
|
||||||
|
if ($payload !== null) {
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($headers !== null) {
|
||||||
|
// Set the headers
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set additional options as needed
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
$error = [
|
||||||
|
'heading' => 'CurlError',
|
||||||
|
'message' => curl_error($ch),
|
||||||
|
'level' => 'error',
|
||||||
|
'ref' => 'curlError',
|
||||||
|
'type' => 'request',
|
||||||
|
];
|
||||||
|
// @todo store error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the cURL session
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateGuid()
|
||||||
|
{
|
||||||
|
$headers = [
|
||||||
|
'Content-Type: application/x-www-form-urlencoded',
|
||||||
|
];
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'guid' => $this->guid,
|
||||||
|
'keyfile' => $this->keyfile,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns {JSON}
|
||||||
|
* hasNewerKeyfile : boolean;
|
||||||
|
* purchaseable: true;
|
||||||
|
* registered: false;
|
||||||
|
* replaceable: false;
|
||||||
|
* upgradeable: false;
|
||||||
|
* upgradeAllowed: string[];
|
||||||
|
* updatesRenewable: false;
|
||||||
|
*/
|
||||||
|
$response = $this->request(
|
||||||
|
self::KEY_SERVER_URL . '/validate/guid',
|
||||||
|
'POST',
|
||||||
|
http_build_query($params),
|
||||||
|
$headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle the response as needed (parsing JSON, etc.)
|
||||||
|
$decodedResponse = json_decode($response, true);
|
||||||
|
|
||||||
|
if (!empty($decodedResponse)) {
|
||||||
|
return $decodedResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo save error response somewhere
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getLatestKey()
|
||||||
|
{
|
||||||
|
$headers = [
|
||||||
|
'Content-Type: application/x-www-form-urlencoded',
|
||||||
|
];
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'keyfile' => $this->keyfile,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns {JSON}
|
||||||
|
* license: string;
|
||||||
|
*/
|
||||||
|
$response = $this->request(
|
||||||
|
self::KEY_SERVER_URL . '/key/latest',
|
||||||
|
'POST',
|
||||||
|
http_build_query($params),
|
||||||
|
$headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle the response as needed (parsing JSON, etc.)
|
||||||
|
$decodedResponse = json_decode($response, true);
|
||||||
|
|
||||||
|
if (!empty($decodedResponse) && !empty($decodedResponse['license'])) {
|
||||||
|
return $decodedResponse['license'];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function installNewKey($key)
|
||||||
|
{
|
||||||
|
require_once "$this->docroot/webGui/include/InstallKey.php";
|
||||||
|
|
||||||
|
$KeyInstaller = new KeyInstaller();
|
||||||
|
$installResponse = $KeyInstaller->installKey($key);
|
||||||
|
|
||||||
|
$installSuccess = false;
|
||||||
|
|
||||||
|
if (!empty($installResponse)) {
|
||||||
|
$decodedResponse = json_decode($installResponse, true);
|
||||||
|
if (isset($decodedResponse['error'])) {
|
||||||
|
$this->writeJsonFile(
|
||||||
|
'/tmp/ReplaceKey/error.json',
|
||||||
|
[
|
||||||
|
'error' => $decodedResponse['error'],
|
||||||
|
'ts' => time(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$installSuccess = false;
|
||||||
|
} else {
|
||||||
|
$installSuccess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$keyType = basename($key, '.key');
|
||||||
|
$output = isset($GLOBALS['notify']) ? _var($GLOBALS['notify'],'plugin') : '';
|
||||||
|
$script = '/usr/local/emhttp/webGui/scripts/notify';
|
||||||
|
|
||||||
|
if ($installSuccess) {
|
||||||
|
$event = "Installed New $keyType License";
|
||||||
|
$subject = "Your new $keyType license key has been automatically installed";
|
||||||
|
$description = "";
|
||||||
|
$importance = "normal $output";
|
||||||
|
} else {
|
||||||
|
$event = "Failed to Install New $keyType License";
|
||||||
|
$subject = "Failed to automatically install your new $keyType license key";
|
||||||
|
$description = isset($decodedResponse['error']) ? $decodedResponse['error'] : "Unknown error occurred";
|
||||||
|
$importance = "alert $output";
|
||||||
|
}
|
||||||
|
|
||||||
|
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg($importance)." -l '/Tools/Registration' -x");
|
||||||
|
|
||||||
|
return $installSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function writeJsonFile($file, $data)
|
||||||
|
{
|
||||||
|
if (!is_dir(dirname($file))) {
|
||||||
|
mkdir(dirname($file));
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check(bool $forceCheck = false)
|
||||||
|
{
|
||||||
|
// we don't need to check
|
||||||
|
if (empty($this->guid) || empty($this->keyfile) || empty($this->regExp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're within the 7-day window before and after regExp
|
||||||
|
$now = time();
|
||||||
|
$sevenDaysBefore = strtotime('-7 days', $this->regExp);
|
||||||
|
$sevenDaysAfter = strtotime('+7 days', $this->regExp);
|
||||||
|
|
||||||
|
$isWithinWindow = ($now >= $sevenDaysBefore && $now <= $sevenDaysAfter);
|
||||||
|
|
||||||
|
if (!$forceCheck && !$isWithinWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if we have a new key
|
||||||
|
$validateGuidResponse = $this->validateGuid();
|
||||||
|
|
||||||
|
$hasNewerKeyfile = @$validateGuidResponse['hasNewerKeyfile'] ?? false;
|
||||||
|
if (!$hasNewerKeyfile) {
|
||||||
|
return; // if there is no newer keyfile, we don't need to do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
$latestKey = $this->getLatestKey();
|
||||||
|
if (!$latestKey) {
|
||||||
|
// we supposedly have a new key, but didn't get it back…
|
||||||
|
$this->writeJsonFile(
|
||||||
|
'/tmp/ReplaceKey/error.json',
|
||||||
|
[
|
||||||
|
'error' => 'Failed to retrieve latest key after getting a `hasNewerKeyfile` in the validation response.',
|
||||||
|
'ts' => time(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->installNewKey($latestKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,7 +108,7 @@ export const WebguiCheckForUpdate = async (): Promise<ServerUpdateOsResponse | u
|
|||||||
params.altUrl = OS_RELEASES.toString();
|
params.altUrl = OS_RELEASES.toString();
|
||||||
}
|
}
|
||||||
const response = await request
|
const response = await request
|
||||||
.url('/plugins/dynamix.plugin.manager/include/UnraidCheck.php')
|
.url('/plugins/dynamix.plugin.manager/include/UnraidCheck.php') // @todo replace with /scripts/unraidcheck
|
||||||
.query(params)
|
.query(params)
|
||||||
.get()
|
.get()
|
||||||
.json((json) => {
|
.json((json) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user