From 8c1621cced3b20e913ebfe160294dbc31b01ce8f Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Fri, 22 Apr 2022 13:17:09 +0200 Subject: [PATCH] fix deprecated jquery calls; move editapikey jq call to Ajax.php; fix edit of apikeys::allowed_from and allow cidr Signed-off-by: Michael Kaufmann --- api_keys.php | 41 ------------ install/lib/class.FroxlorInstall.php | 5 +- install/lng/english.lng.php | 1 + install/lng/german.lng.php | 1 + lib/Froxlor/Ajax/Ajax.php | 66 +++++++++++++++++-- lib/Froxlor/Api/Response.php | 28 ++++---- lib/Froxlor/UI/Callbacks/Text.php | 1 + lib/formfields/formfield.api_key.php | 8 +-- lng/english.lng.php | 6 +- lng/german.lng.php | 4 +- .../Froxlor/src/js/components/apikeys.js | 57 ++++++++++++++++ .../Froxlor/src/js/components/configfiles.js | 4 +- .../Froxlor/src/js/components/customer.js | 6 +- .../Froxlor/src/js/components/domains.js | 8 +-- .../Froxlor/src/js/components/ipsandports.js | 4 +- .../Froxlor/src/js/components/newsfeed.js | 2 +- templates/Froxlor/src/js/components/search.js | 4 +- .../Froxlor/src/js/components/updatecheck.js | 2 +- templates/Froxlor/src/js/main.js | 3 +- templates/Froxlor/table/macros.html.twig | 2 +- 20 files changed, 168 insertions(+), 85 deletions(-) create mode 100644 templates/Froxlor/src/js/components/apikeys.js diff --git a/api_keys.php b/api_keys.php index d57fa51b..55c187e8 100644 --- a/api_keys.php +++ b/api_keys.php @@ -83,47 +83,6 @@ if ($action == 'delete') { 'cid' => $cid )); $success_message = $lng['apikeys']['apikey_added']; -} elseif ($action == 'jqEditApiKey') { - $keyid = isset($_POST['id']) ? (int) $_POST['id'] : 0; - $allowed_from = isset($_POST['allowed_from']) ? $_POST['allowed_from'] : ""; - $valid_until = isset($_POST['valid_until']) ? (int) $_POST['valid_until'] : -1; - - // validate allowed_from - if (!empty($allowed_from)) { - $ip_list = array_map('trim', explode(",", $allowed_from)); - $_check_list = $ip_list; - foreach ($_check_list as $idx => $ip) { - if (\Froxlor\Validate\Validate::validate_ip2($ip, true, 'invalidip', true, true) == false) { - unset($ip_list[$idx]); - } - } - $ip_list = array_map('inet_ntop', array_map('inet_pton', $ip_list)); - $allowed_from = implode(",", array_unique($ip_list)); - } - - if ($valid_until <= 0 || !is_numeric($valid_until)) { - $valid_until = -1; - } - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_API_KEYS . "` SET - `valid_until` = :vu, `allowed_from` = :af - WHERE `id` = :keyid AND `adminid` = :aid AND `customerid` = :cid - "); - if (AREA == 'admin') { - $cid = 0; - } elseif (AREA == 'customer') { - $cid = $userinfo['customerid']; - } - Database::pexecute($upd_stmt, array( - 'keyid' => $keyid, - 'af' => $allowed_from, - 'vu' => $valid_until, - 'aid' => $userinfo['adminid'], - 'cid' => $cid - )); - echo json_encode(true); - exit(); } $log->logAction(\Froxlor\FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed api::api_keys"); diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 89d8d8bc..8469718d 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -444,7 +444,7 @@ class FroxlorInstall `vhostcontainer` = '1', `vhostcontainer_servername_statement` = '1' "); - $nvh = $this->_data['webserver'] == 'apache2' ? '1' : '0'; + $nvh = $this->_data['webserver'] == 'apache2' ? '1' : '0'; $stmt->execute(array( 'nvh' => $nvh, 'serverip' => $this->_data['serverip'], @@ -1193,6 +1193,9 @@ class FroxlorInstall // check for json extension $this->_requirementCheckFor($content, $_die, 'json', false, 'phpjson'); + // check for gmp extension + $this->_requirementCheckFor($content, $_die, 'gmp', false, 'phpgmp'); + // check for bcmath extension $this->_requirementCheckFor($content, $_die, 'bcmath', true, 'phpbcmath', 'bcmathdescription'); diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php index 73ef9d4e..031c6ff8 100644 --- a/install/lng/english.lng.php +++ b/install/lng/english.lng.php @@ -36,6 +36,7 @@ $lng['requirements']['phpcurl'] = 'PHP curl-extension...'; $lng['requirements']['phpmbstring'] = 'PHP mbstring-extension...'; $lng['requirements']['phpzip'] = 'PHP zip-extension...'; $lng['requirements']['phpjson'] = 'PHP json-extension...'; +$lng['requirements']['phpgmp'] = 'PHP gmp-extension...'; $lng['requirements']['bcmathdescription'] = 'Traffic-calculation related functions will not work correctly!'; $lng['requirements']['zipdescription'] = 'The auto-update feature requires the zip extension.'; $lng['requirements']['openbasedir'] = 'open_basedir...'; diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php index a3ac7c6a..0a1226ed 100644 --- a/install/lng/german.lng.php +++ b/install/lng/german.lng.php @@ -36,6 +36,7 @@ $lng['requirements']['phpcurl'] = 'PHP curl-Erweiterung...'; $lng['requirements']['phpmbstring'] = 'PHP mbstring-Erweiterung...'; $lng['requirements']['phpzip'] = 'PHP zip-Erweiterung...'; $lng['requirements']['phpjson'] = 'PHP json-Erweiterung...'; +$lng['requirements']['phpgmp'] = 'PHP gmp-Erweiterung...'; $lng['requirements']['bcmathdescription'] = 'Traffic-Berechnungs bezogene Funktionen stehen nicht vollständig zur Verfügung!'; $lng['requirements']['zipdescription'] = 'Die Auto-Update Funktion benötigt die zip Erweiterung.'; $lng['requirements']['openbasedir'] = 'open_basedir genutzt wird...'; diff --git a/lib/Froxlor/Ajax/Ajax.php b/lib/Froxlor/Ajax/Ajax.php index de9123a5..e5c8ee95 100644 --- a/lib/Froxlor/Ajax/Ajax.php +++ b/lib/Froxlor/Ajax/Ajax.php @@ -3,7 +3,9 @@ namespace Froxlor\Ajax; use Exception; +use Froxlor\Database\Database; use Froxlor\Http\HttpClient; +use Froxlor\Validate\Validate; use Froxlor\Settings; use Froxlor\UI\Listing; use Froxlor\UI\Panel\UI; @@ -52,7 +54,7 @@ class Ajax global $lng; // query the whole table - $result_stmt = \Froxlor\Database\Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); + $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); $langs = array(); // presort languages @@ -110,6 +112,8 @@ class Ajax return $this->searchGlobal(); case 'tablelisting': return $this->updateTablelisting(); + case 'editapikey': + return $this->editApiKey(); default: return $this->errorResponse('Action not found!'); } @@ -236,8 +240,7 @@ class Ajax $result = array_merge($result_settings, $result_entities); - header("Content-type: application/json"); - echo json_encode($result); + return $this->jsonResponse($result); } private function updateTablelisting() @@ -249,6 +252,61 @@ class Ajax Listing::storeColumnListingForUser([Request::get('listing') => $columns]); - return json_encode($columns); + return $this->jsonResponse($columns); + } + + private function editApiKey() + { + $keyid = isset($_POST['id']) ? (int) $_POST['id'] : 0; + $allowed_from = isset($_POST['allowed_from']) ? $_POST['allowed_from'] : ""; + $valid_until = isset($_POST['valid_until']) ? (int) $_POST['valid_until'] : -1; + + // validate allowed_from + if (!empty($allowed_from)) { + $ip_list = array_map('trim', explode(",", $allowed_from)); + $_check_list = $ip_list; + foreach ($_check_list as $idx => $ip) { + if (Validate::validate_ip2($ip, true, 'invalidip', true, true, true) == false) { + unset($ip_list[$idx]); + continue; + } + // check for cidr + if (strpos($ip, '/') !== false) { + $ipparts = explode("/", $ip); + // shorten IP + $ip = inet_ntop(inet_pton($ipparts[0])); + // re-add cidr + $ip .= '/' . $ipparts[1]; + } else { + // shorten IP + $ip = inet_ntop(inet_pton($ip)); + } + $ip_list[$idx] = $ip; + } + $allowed_from = implode(",", array_unique($ip_list)); + } + + if ($valid_until <= 0 || !is_numeric($valid_until)) { + $valid_until = -1; + } + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_API_KEYS . "` SET + `valid_until` = :vu, `allowed_from` = :af + WHERE `id` = :keyid AND `adminid` = :aid AND `customerid` = :cid + "); + if ((int) $this->userinfo['adminsession'] == 1) { + $cid = 0; + } else { + $cid = $this->userinfo['customerid']; + } + Database::pexecute($upd_stmt, array( + 'keyid' => $keyid, + 'af' => $allowed_from, + 'vu' => $valid_until, + 'aid' => $this->userinfo['adminid'], + 'cid' => $cid + )); + return $this->jsonResponse(['allowed_from' => $allowed_from, 'valid_until' => $valid_until]); } } diff --git a/lib/Froxlor/Api/Response.php b/lib/Froxlor/Api/Response.php index a88cb795..c03d4b29 100644 --- a/lib/Froxlor/Api/Response.php +++ b/lib/Froxlor/Api/Response.php @@ -1,4 +1,5 @@ $data], $response_code); - } + public static function jsonDataResponse($data = null, int $response_code = 200) + { + return self::jsonResponse(['data' => $data], $response_code); + } - public static function jsonErrorResponse($message = null, int $response_code = 400) - { - return self::jsonResponse(['message' => $message], $response_code); - } + public static function jsonErrorResponse($message = null, int $response_code = 400) + { + return self::jsonResponse(['message' => $message], $response_code); + } } diff --git a/lib/Froxlor/UI/Callbacks/Text.php b/lib/Froxlor/UI/Callbacks/Text.php index 12e6ead0..6f1c3156 100644 --- a/lib/Froxlor/UI/Callbacks/Text.php +++ b/lib/Froxlor/UI/Callbacks/Text.php @@ -79,6 +79,7 @@ class Text 'editid' => $attributes['fields']['id'] ]); return [ + 'entry' => $attributes['fields']['id'], 'id' => 'akModal' . $attributes['fields']['id'], 'title' => 'API-key ' . ($attributes['fields']['loginname'] ?? $attributes['fields']['adminname']), 'body' => $body diff --git a/lib/formfields/formfield.api_key.php b/lib/formfields/formfield.api_key.php index 2bff23a4..3e96fe33 100644 --- a/lib/formfields/formfield.api_key.php +++ b/lib/formfields/formfield.api_key.php @@ -40,15 +40,15 @@ return [ 'value' => $result['secret'] ], 'allowed_from' => [ - 'label' => UI::getLng('apikeys.allowed_from'), + 'label' => ['title' => UI::getLng('apikeys.allowed_from'), 'description' => UI::getLng('apikeys.allowed_from_help')], 'type' => 'text', 'value' => $result['allowed_from'], ], 'valid_until' => [ - 'label' => UI::getLng('apikeys.valid_until'), + 'label' => ['title' => UI::getLng('apikeys.valid_until'), 'description' => UI::getLng('apikeys.valid_until_help')], + /** @TODO datetime-picker */ 'type' => 'text', - 'value' => $result['valid_until'], - 'format_callback' => [Text::class, 'timestampUntil'], + 'value' => $result['valid_until'] < 0 ? "" : $result['valid_until'] ] ] ] diff --git a/lng/english.lng.php b/lng/english.lng.php index 43fb1ca1..629438da 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2041,7 +2041,7 @@ $lng['apikeys']['apikey_removed'] = 'The api key with the id #%s has been remove $lng['apikeys']['apikey_added'] = 'A new api key has been generated successfully'; $lng['apikeys']['clicktoview'] = 'Click to view'; $lng['apikeys']['allowed_from'] = 'Allowed from'; -$lng['apikeys']['allowed_from_help'] = 'Comma separated list of ip addresses. Default empty.
Specifying a subnet e.g. 192.168.1.1/24 is currently not supported.'; +$lng['apikeys']['allowed_from_help'] = 'Comma separated list of ip addresses / networks.
Default is empty (allow from all).'; $lng['apikeys']['valid_until'] = 'Valid until'; $lng['apikeys']['valid_until_help'] = 'Date until valid, format YYYY-MM-DD'; $lng['serversettings']['enable_api']['title'] = 'Enable external API usage'; @@ -2167,5 +2167,5 @@ $lng['panel']['settingsmodebasic'] = 'Basic'; $lng['panel']['settingsmodeadvanced'] = 'Advanced'; $lng['panel']['settingsmodetoggle'] = 'Click to toggle mode'; $lng['panel']['modalclose'] = 'Close'; -$lng['panel']['managetablecolumnsmodal']['title'] = 'Manage Table columns'; -$lng['panel']['managetablecolumnsmodal']['description'] = 'Here you can individualise the table columns for yourself.'; +$lng['panel']['managetablecolumnsmodal']['title'] = 'Manage table columns'; +$lng['panel']['managetablecolumnsmodal']['description'] = 'Here you can customize the visible columns'; diff --git a/lng/german.lng.php b/lng/german.lng.php index f9720c73..51299826 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1683,7 +1683,7 @@ $lng['apikeys']['no_api_keys'] = 'Keine API Keys gefunden'; $lng['apikeys']['key_add'] = 'API Key hinzufügen'; $lng['apikeys']['apikey_removed'] = 'Der API Key mit der ID #%s wurde erfolgreich gelöscht.'; $lng['apikeys']['allowed_from'] = 'Erlaube Zugriff von'; -$lng['apikeys']['allowed_from_help'] = 'Komma getrennte Liste von IPs. Standard ist leer.
Angabe von Subnetzen z.B. 192.168.1.1/24 wird derzeit nicht unterstützt.'; +$lng['apikeys']['allowed_from_help'] = 'Komma getrennte Liste von IPs oder Netzen.
Standard ist leer (von überall erlaubt).'; $lng['apikeys']['valid_until'] = 'Gültig bis'; $lng['apikeys']['valid_until_help'] = 'Datum Gültigkeitsende, Format JJJJ-MM-TT'; $lng['serversettings']['enable_api']['title'] = 'Aktiviere externe API Nutzung'; @@ -1806,4 +1806,4 @@ $lng['panel']['settingsmodeadvanced'] = 'Erweitert'; $lng['panel']['settingsmodetoggle'] = 'Modus umschalten'; $lng['panel']['modalclose'] = 'Schließen'; $lng['panel']['managetablecolumnsmodal']['title'] = 'Tabellenspalten verwalten'; -$lng['panel']['managetablecolumnsmodal']['description'] = 'Hier kannst du die Tabellenspalten für dich selbst individualisieren.'; +$lng['panel']['managetablecolumnsmodal']['description'] = 'Hier können die angezeigten Tabellenspalten angepasst werden'; diff --git a/templates/Froxlor/src/js/components/apikeys.js b/templates/Froxlor/src/js/components/apikeys.js new file mode 100644 index 00000000..41db9028 --- /dev/null +++ b/templates/Froxlor/src/js/components/apikeys.js @@ -0,0 +1,57 @@ +$(function () { + + var timer, delay = 500; + $('div[data-action="apikeys"] #allowed_from').on('keyup change', function () { + var _this = $(this); + clearTimeout(timer); + timer = setTimeout(function () { + var akid = $('div[data-action="apikeys"]').data('entry'); + $.ajax({ + url: "lib/ajax.php?action=editapikey", + type: "POST", + dataType: "json", + data: { id: akid, allowed_from: _this.val(), valid_until: $('div[data-action="apikeys"] #valid_until').val() }, + success: function (data) { + if (data.message) { + _this.removeClass('is-valid'); + _this.addClass('is-invalid'); + } else { + _this.removeClass('is-invalid'); + _this.addClass('is-valid'); + _this.val(data.allowed_from); + } + }, + error: function (request, status, error) { + console.log(request, status, error) + } + }); + }, delay); + }); + + $('div[data-action="apikeys"] #valid_until').on('keyup change', function () { + var _this = $(this); + clearTimeout(timer); + timer = setTimeout(function () { + var akid = $('div[data-action="apikeys"]').data('entry'); + $.ajax({ + url: "lib/ajax.php?action=editapikey", + type: "POST", + dataType: "json", + data: { id: akid, valid_until: _this.val(), allowed_from: $('div[data-action="apikeys"] #allowed_from').val() }, + success: function (data) { + if (data.message) { + _this.removeClass('is-valid'); + _this.addClass('is-invalid'); + } else { + _this.removeClass('is-invalid'); + _this.addClass('is-valid'); + _this.val(data.valid_until); + } + }, + error: function (request, status, error) { + console.log(request, status, error) + } + }); + }, delay); + }); +}); diff --git a/templates/Froxlor/src/js/components/configfiles.js b/templates/Froxlor/src/js/components/configfiles.js index 6ea59352..2a7f97b4 100644 --- a/templates/Froxlor/src/js/components/configfiles.js +++ b/templates/Froxlor/src/js/components/configfiles.js @@ -1,8 +1,8 @@ -$(document).ready(function () { +$(function() { /* * config files - select all recommended */ - $('#selectRecommendedConfig').click(function () { + $('#selectRecommendedConfig').on('click', function () { $('input[data-recommended]').each(function () { if ($(this).data('recommended') == 1) { $(this).prop('checked', true); diff --git a/templates/Froxlor/src/js/components/customer.js b/templates/Froxlor/src/js/components/customer.js index f04e1e0f..eefe02af 100644 --- a/templates/Froxlor/src/js/components/customer.js +++ b/templates/Froxlor/src/js/components/customer.js @@ -1,4 +1,4 @@ -$(document).ready(function () { +$(function() { // Make inputs with enabled unlimited checked disabled $("input[name$='_ul']").each(function () { @@ -9,7 +9,7 @@ $(document).ready(function () { }); }); // change state when unlimited checkboxes are clicked - $("input[name$='_ul']").change(function () { + $("input[name$='_ul']").on('change', function () { var fieldname = $(this).attr("name").substring(0, $(this).attr("name").length - 3); $("input[name='" + fieldname + "']").prop({ readonly: $(this).is(":checked"), @@ -21,7 +21,7 @@ $(document).ready(function () { }); // set values from hosting plan when adding/editing a customer according to the plan's values - $('#use_plan').change(function () { + $('#use_plan').on('change', function () { var pid = $(this).val(); if (pid > 0) { $.ajax({ diff --git a/templates/Froxlor/src/js/components/domains.js b/templates/Froxlor/src/js/components/domains.js index cd083411..67817d54 100644 --- a/templates/Froxlor/src/js/components/domains.js +++ b/templates/Froxlor/src/js/components/domains.js @@ -1,7 +1,7 @@ -$(document).ready(function () { +$(function() { // disable unusable php-configuration by customer settings - $('#customerid').change(function () { + $('#customerid').on('change', function () { var cid = $(this).val(); $.ajax({ url: "admin_domains.php?page=domains&action=jqGetCustomerPHPConfigs", @@ -31,7 +31,7 @@ $(document).ready(function () { // show warning if speciallogfile option is toggled if ($('input[name=speciallogverified]')) { - $('input[name=speciallogfile]').click(function () { + $('input[name=speciallogfile]').on('click', function () { $('#speciallogfilenote').remove(); $('#speciallogfile').removeClass('is-invalid'); $('#speciallogverified').val(0); @@ -64,7 +64,7 @@ $(document).ready(function () { $('#section_d').hide(); } - $('#email_only').click(function () { + $('#email_only').on('click', function () { if ($(this).is(':checked')) { // hide unnecessary sections $('#section_b').hide(); diff --git a/templates/Froxlor/src/js/components/ipsandports.js b/templates/Froxlor/src/js/components/ipsandports.js index 50c40a7a..c42b6da4 100644 --- a/templates/Froxlor/src/js/components/ipsandports.js +++ b/templates/Froxlor/src/js/components/ipsandports.js @@ -1,7 +1,7 @@ -$(document).ready(function () { +$(function() { // check for internal ip and output a notice if private-range ip is given - $('#ip').change(function () { + $('#ip').on('change', function () { var ipval = $(this).val(); if (ipval.length > 0) { $('#ipnote').remove(); diff --git a/templates/Froxlor/src/js/components/newsfeed.js b/templates/Froxlor/src/js/components/newsfeed.js index 8e4fb8c2..2776eeb3 100644 --- a/templates/Froxlor/src/js/components/newsfeed.js +++ b/templates/Froxlor/src/js/components/newsfeed.js @@ -1,4 +1,4 @@ -$(document).ready(function () { +$(function() { /* * newsfeed */ diff --git a/templates/Froxlor/src/js/components/search.js b/templates/Froxlor/src/js/components/search.js index 40dd2c06..92115aab 100644 --- a/templates/Froxlor/src/js/components/search.js +++ b/templates/Froxlor/src/js/components/search.js @@ -1,7 +1,7 @@ -$(document).ready(function () { +$(function() { let search = $('#search') - search.submit(function (e) { + search.on('submit', function (e) { e.preventDefault(); }); diff --git a/templates/Froxlor/src/js/components/updatecheck.js b/templates/Froxlor/src/js/components/updatecheck.js index e5da6103..9a7cae69 100644 --- a/templates/Froxlor/src/js/components/updatecheck.js +++ b/templates/Froxlor/src/js/components/updatecheck.js @@ -1,4 +1,4 @@ -$(document).ready(function () { +$(function() { /* * updatecheck */ diff --git a/templates/Froxlor/src/js/main.js b/templates/Froxlor/src/js/main.js index 613523ed..a548ef2b 100644 --- a/templates/Froxlor/src/js/main.js +++ b/templates/Froxlor/src/js/main.js @@ -6,7 +6,7 @@ import 'chart.js/dist/chart'; // load jquery global.$ = require('jquery'); -$(document).ready(function () { +$(function() { window.$theme = 'Froxlor'; }); @@ -19,3 +19,4 @@ require('./components/tablecolumns') require('./components/ipsandports') require('./components/domains') require('./components/configfiles') +require('./components/apikeys') diff --git a/templates/Froxlor/table/macros.html.twig b/templates/Froxlor/table/macros.html.twig index 3944e548..0908277a 100644 --- a/templates/Froxlor/table/macros.html.twig +++ b/templates/Froxlor/table/macros.html.twig @@ -53,7 +53,7 @@ {% endif %} {% if data.modal is defined and data.modal is iterable %} -