Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cnic registrar module): patched url forwarding bug and improved user experience #208

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/linters/phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
parameters:
level: 5
#phpVersion: 80100
#treatPhpDocTypesAsCertain: false
fileExtensions:
- php
- module
- inc
paths:
- ../../components/modules/cnr
excludePaths:
- ../../components/modules/cnr/apis/vendor
scanDirectories:
# - /usr/share/rtldev-middleware-blesta/compon
ents
- /var/www/html/blesta
bootstrapFiles:
- /var/www/html/blesta/lib/init.php
#- /var/www/html/blesta/vendors/autoload.php
ignoreErrors:
#- "#^Access to an undefined property WHMCS\\\\Domain\\\\Domain::\\$.*.$#"
#- "#^Call to an undefined static method WHMCS\\\\Domain\\\\Domain::.+\\(\\).$#"
#- "#^Method WHMCS\\\\View\\\\Menu\\\\Item::getChild\\(\\) invoked with 1 parameter, 2 required.$#"
#-
# message: '#Access to an undefined property [a-zA-Z0-9\\_]+::\$(autosetup|code|status|remoteid|id|gid)#'
# paths:
# - ../../modules/addons/cnicssl_addon/cnicssl_addon.php
# - ../../modules/servers/cnicssl/cnicssl.php
# - ../../modules/servers/cnicssl/lib/SSLHelper.php
#- '#Function [a-zA-Z0-9\\_]+ not found.#'
#- '#Variable \$[a-zA-Z0-9\\_]+ might not be defined.#'
#- '#Undefined variable: \$[a-zA-Z0-9\\_]+#'
#- '#Access to an undefined property object::#'
#- '#Parameter \#2 \$callback of function array_walk expects callable#'
parallel:
maximumNumberOfProcesses: 1000
tmpDir: /tmp/phpstan
8 changes: 3 additions & 5 deletions components/modules/cnr/cnr.php
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ public function getExpirationDate($service, $format = 'Y-m-d H:i:s')
Loader::loadHelpers($this, ['Date']);

$domain = $this->getServiceDomain($service);
$row = $this->getModuleRow($service->module_row_id ?? $service->module_row);
$row = $this->getModuleRow($service->module_row_id ?? $service->module_row) ?? $this->getModuleRows()[0];
Base::setModule($row);
Base::moduleInstance($this);

Expand Down Expand Up @@ -667,7 +667,6 @@ public function addPackage(array $vars = null)
];
}
}

return $meta;
}

Expand Down Expand Up @@ -1273,7 +1272,7 @@ public function getAdminEditFields($package, $vars = null)

$fields = new ModuleFields();

Base::setModule($this->getModuleRow($package->module_row));
Base::setModule($this->getModuleRow($package->module_row) ?? $this->getModuleRows()[0]);
Base::moduleInstance($this);

if ($package->meta->type === "domain" && $_SERVER["REQUEST_METHOD"] === "GET") {
Expand Down Expand Up @@ -1958,7 +1957,6 @@ private function manageSettings(

// check if id_protection is enabled for the domain
$vars->whois_privacy = !$this->featureServiceEnabled('id_protection', $service) ? "disabled" : $vars->whois_privacy;

// To get epp/auth code
if (isset($post["request_epp"])) {
// Expiring Authorization Codes
Expand Down Expand Up @@ -2205,7 +2203,7 @@ public function getServiceDomain($service)
*/
public function getTldPricing($module_row_id = null)
{
$this->getFilteredTldPricing($module_row_id);
return $this->getFilteredTldPricing($module_row_id);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion components/modules/cnr/lib/AdditionalFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,11 @@ private function getDropdownOptions($name, $values, $labels, $required)
// } elseif ($name === "X-EU-REGISTRANT-CITIZENSHIP") {
// $options[$val] = $val . "|" . Language::_($val, $labels[$idx], true); // todo, fallback logic to translation file
// } else {
$options[$val] = $val . "|" . $this->getTranslation($name, $val, $labels[$idx], true, true);
$options[$val] = $val;
$valTranslation = $this->getTranslation($name, $val, $labels[$idx], true, true);
if ($val !== $valTranslation) {
$options[$val] = $val . "|" . $valTranslation;
}
// }
}
}
Expand Down
18 changes: 16 additions & 2 deletions components/modules/cnr/lib/DomainManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,18 +228,32 @@ public function getZoneInfo($domains)
public function addDNS($domain, $postData)
{
$record = Helper::getResourceRecord($postData, $domain);
if (array_key_exists("record_type", $record) && in_array($record["record_type"], ["RD", "MRD"])) {
return $this->call([
"COMMAND" => "AddWebFwd",
"source" => $record["hostname"],
"target" => $record["address"],
"type" => $record["record_type"]
]);
}
return $this->call([
"COMMAND" => "ModifyDNSZone",
"DNSZONE" => "{$domain}",
"ADDRR0" => "{$record}"
]);
}

public function deleteDNS($dns, $postData)
public function deleteDNS($domain, $postData)
{
if ($postData["raw_record"] == "URL" || $postData["raw_record"] == "FRAME") {
return $this->call([
"COMMAND" => "DeleteWebFwd",
"source" => $domain
]);
}
return $this->call([
"COMMAND" => "ModifyDNSZone",
"DNSZONE" => "{$dns}",
"DNSZONE" => "{$domain}",
"DELRR0" => "{$postData['raw_record']}"
]);
}
Expand Down
120 changes: 67 additions & 53 deletions components/modules/cnr/lib/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,39 @@
use CNR\MODULE\LIB\Base;
use CNIC\IDNA\Factory\ConverterFactory;

define("CNIC_TLD_CACHE", "1 day");
if (!defined("CNIC_TLD_CACHE")) {
define("CNIC_TLD_CACHE", "1 day");
}

class Helper
{
public static function getResourceRecord($record, $domain = "")
/**
* Retrieves the resource record for a given domain.
*
* @param array $record resource record with their respective data.
* @param string $domain The domain name for which to retrieve the resource record. Default is an empty string.
* @return mixed The resource record data for the specified domain and record type.
*/
public static function getResourceRecord(array $record, string $domain = "")
{
// hostname ttl IN record_type priority address
// $record = $data['hostname'] . " " . $data['ttl'] . " IN " . $data['record_type'];
// if (isset($data['priority']) && !empty($data['priority']) && ($data['record_type'] == 'MX' || $data['record_type'] == 'MXE' || $data['record_type'] == 'SRV')) {
// $record .= " " . $data['priority'];
// }
// $record .= " " . $data['address'];

// Determine record to add
$zone = [];
$ttl = $record["ttl"] ?? "3600";
if (!is_numeric($ttl)) {
$ttl = "3600";
}
if (!$record['hostname'] || (isset($domain) && $record['hostname'] === $domain)) {

if (!$record['hostname'] || (!empty($domain) && $record['hostname'] === $domain)) {
$record['hostname'] = "@";
}

switch ($record['record_type']) {
case "URL":
case "FRAME":
$record["hostname"] = ($record["hostname"] == "@") ? $domain : $record["hostname"] . "." . $domain;
$record["record_type"] = $record["record_type"] == "URL" ? "RD" : "MRD";
$zone[] = $record;
break;
case "MXE":
$address = $record["address"];
$mxpref = is_numeric($record["priority"]) ? $record["priority"] : "100";
Expand Down Expand Up @@ -61,7 +70,14 @@ public static function getResourceRecord($record, $domain = "")
return $zone;
}

public static function errorHandler($response, $successCodes = "/^200$/")
/**
* Handles errors based on the response and success codes.
*
* @param array|\stdClass|null $response The response to be evaluated. Expected to be an associative array or an object with a 'CODE' key and optionally an 'error' key.
* @param string $successCodes A regex pattern representing the success codes. Defaults to "/^200$/".
* @return boolean
*/
public static function errorHandler(array|\stdClass|null $response, string $successCodes = "/^200$/")
{
if ($response instanceof \stdClass) {
$response = json_decode(json_encode($response), true);
Expand Down Expand Up @@ -93,7 +109,7 @@ public static function errorHandler($response, $successCodes = "/^200$/")
* @param string $domain
* @return array
*/
public static function getResourceRecords($resourceRecords, $domain = "")
public static function getResourceRecords(array $resourceRecords, string $domain = "")
{
if (empty($resourceRecords)) {
return [];
Expand All @@ -119,61 +135,49 @@ public static function getResourceRecords($resourceRecords, $domain = "")
continue;
}

if ((bool)preg_match("/^(A|AAAA|CNAME)$/", $rrtype)) {
if (
$rrtype !== "A" ||
!preg_match("/^mxe-host-for-ip-(\d+)-(\d+)-(\d+)-(\d+)$/i", $domain, $m)
) {
$hostrecords[$i] = [
"hostname" => $name,
"ttl" => $ttl,
"record_type" => $rrtype,
"address" => $content
];
$hostrecords[$i]["raw_record"] = Helper::getResourceRecord($hostrecords[$i]);
continue;
if ($resourceRecords['LOCKED'][$i] == 1) {
$fwdtype = "URL";
if (strtolower($rrtype) == "mrd") {
$fwdtype = "FRAME";
}
}

if ($rrtype === "SRV" || $rrtype === "MX") {
$hostrecords[$i] = [
"hostname" => $name,
"ttl" => $ttl,
"record_type" => $rrtype,
"address" => $content,
"priority" => $priority
'hostname' => $name,
'record_type' => $fwdtype,
'address' => $content
];
$hostrecords[$i]["raw_record"] = Helper::getResourceRecord($hostrecords[$i]);
$hostrecords[$i]["raw_record"] = $fwdtype;
continue;
}

if ($rrtype === "X-HTTP") {
if ($rrtype === "REDIRECT") {
$url_type = "URL";
if ($rrtype == 'MX') {
if ($content == $priority) {
continue;
}
if (substr($content, 0, strlen($priority)) === $priority) {
$content = substr($content, strlen($priority) + 1);
}

$hostrecords[$i] = [
"hostname" => $name,
"ttl" => $ttl,
"record_type" => $url_type,
"address" => $content
];
$hostrecords[$i]["raw_record"] = Helper::getResourceRecord($hostrecords[$i]);
continue;
}


// TXT or other records
$hostrecords[$i] = [
"hostname" => $name,
"ttl" => $ttl,
"record_type" => $rrtype,
"address" => $content
];
$hostrecords[$i]["raw_record"] = Helper::getResourceRecord($hostrecords[$i]);
$hostrecords[$i]["raw_record"] = Helper::getResourceRecord($hostrecords[$i], $domain);
}
return $hostrecords;
}

/**
* Retrieves the supported Resource Record (RR) types.
*
* This method returns an array of supported DNS Resource Record types.
*
* @return array An array of supported RR types.
*/
public static function getSupportedRRTypes()
{
return [
Expand All @@ -192,17 +196,19 @@ public static function getSupportedRRTypes()
"SRV",
"TXT",
"TLSA",
"X-HTTP"
"URL",
"FRAME"
];
}

/**
* Builds and returns the rules required to add/edit a module row
*
* @param array $vars An array of key/value data pairs
* @param int|null $module_row_id The module row ID to validate against
* @return array An array of Input rules suitable for Input::setRules()
*/
public static function getRowRules(&$vars, $module_row_id = null)
public static function getRowRules(array &$vars, $module_row_id = null)
{
$instance = Base::moduleInstance();
return [
Expand Down Expand Up @@ -238,6 +244,7 @@ public static function getRowRules(&$vars, $module_row_id = null)
* retrieve data from cache if available.
*
* @param string $keyName The key name for the cache.
* @param bool $returnAsArray Whether to return the cached data as an array.
* @return mixed|false The cached data or false if caching is disabled or an error occurs.
*/
public static function hasCache(string $keyName, bool $returnAsArray = false)
Expand Down Expand Up @@ -266,6 +273,7 @@ public static function hasCache(string $keyName, bool $returnAsArray = false)
*
* @param string $keyName The key name for the cache.
* @param object|array $cacheData The data to store in cache.
* @param int|null $ttl The time-to-live for the cache data. Default is null.
* @return bool True if the data was successfully cached, false otherwise.
*/
public static function setCache(string $keyName, object|array $cacheData, $ttl = null)
Expand Down Expand Up @@ -297,15 +305,21 @@ public static function setCache(string $keyName, object|array $cacheData, $ttl =
return self::hasCache($keyName);
}

/**
* Clears the cache for the given key name.
*
* @param string $keyName The name of the cache key to clear.
* @return void
*/
public static function clearCache(string $keyName)
{
// If cache is disabled or key name is empty, return false
if (!\Configure::get("Caching.on") || empty($keyName)) {
return false;
return;
}

// Clear the cache
return \Cache::clearCache(
\Cache::clearCache(
$keyName,
\Configure::get("Blesta.company_id") . \DS . "modules" . \DS . "cnr" . \DS
);
Expand All @@ -317,7 +331,7 @@ public static function clearCache(string $keyName)
* @param array|string $periods Periods data from API response.
* @return array Parsed periods.
*/
public static function parsePeriods($periods)
public static function parsePeriods(array|string $periods)
{
return array_values(array_unique( // unique values, re-indexed
array_map( // convert strings to ints
Expand All @@ -340,7 +354,7 @@ public static function parsePeriods($periods)
* @param array $domains list of domain names (or tlds)
* @return array
*/
public static function IDNConvert($data)
public static function IDNConvert(array $data)
{
return ConverterFactory::convert($data);
}
Expand Down
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"centralnic-reseller/php-sdk": "^9.0.1"
},
"require-dev": {
"squizlabs/php_codesniffer": "^3.7"
"squizlabs/php_codesniffer": "^3.7",
"phpstan/phpstan": "^1.12"
},
"license": "MIT",
"authors": [
Expand All @@ -24,6 +25,10 @@
}
],
"scripts": {
"phpstan": [
"@composer update",
"phpstan analyze -vvv -c .github/linters/phpstan.neon --memory-limit 10G"
],
"codecheck": [
"@composer update",
"phpcs --standard=PSR12 -q -n -s --colors --ignore=node_modules,vendor,build,tmp components"
Expand All @@ -42,4 +47,4 @@
"CNR\\MODULE\\LIB\\": "components/modules/cnr/lib/"
}
}
}
}