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 <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann
2022-04-22 13:17:09 +02:00
parent a615b04eb4
commit 8c1621cced
20 changed files with 168 additions and 85 deletions

View File

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

View File

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

View File

@@ -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...';

View File

@@ -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...';

View File

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

View File

@@ -1,4 +1,5 @@
<?php
namespace Froxlor\Api;
/**
@@ -18,20 +19,21 @@ namespace Froxlor\Api;
*/
class Response
{
public static function jsonResponse($data = null, int $response_code = 200)
{
http_response_code($response_code);
public static function jsonResponse($data = null, int $response_code = 200)
{
header("Content-Type: application/json");
http_response_code($response_code);
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
public static function jsonDataResponse($data = null, int $response_code = 200)
{
return self::jsonResponse(['data' => $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);
}
}

View File

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

View File

@@ -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']
]
]
]

View File

@@ -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.<br>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.<br>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';

View File

@@ -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.<br>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.<br>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';

View File

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

View File

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

View File

@@ -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({

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
$(document).ready(function () {
$(function() {
/*
* newsfeed
*/

View File

@@ -1,7 +1,7 @@
$(document).ready(function () {
$(function() {
let search = $('#search')
search.submit(function (e) {
search.on('submit', function (e) {
e.preventDefault();
});

View File

@@ -1,4 +1,4 @@
$(document).ready(function () {
$(function() {
/*
* updatecheck
*/

View File

@@ -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')

View File

@@ -53,7 +53,7 @@
{% endif %}
</a>
{% if data.modal is defined and data.modal is iterable %}
<div class="modal fade" id="{{ data.modal.id }}" aria-hidden="true" aria-labelledby="{{ data.modal.id }}Label" tabindex="-1">
<div class="modal fade" data-action="apikeys" data-entry="{{ data.modal.entry }}" id="{{ data.modal.id }}" aria-hidden="true" aria-labelledby="{{ data.modal.id }}Label" tabindex="-1">
<div class="modal-dialog {{ data.modal.size|default('modal-xl') }} modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">