merge branch '0.11-dev' of github.com:Froxlor/Froxlor into 0.11-dev

This commit is contained in:
envoyr
2022-04-30 11:59:58 +02:00
13 changed files with 202 additions and 29 deletions

View File

@@ -27,6 +27,7 @@
declare(strict_types=1);
use Symfony\Component\Console\Application;
use Froxlor\Cli\RunApiCommand;
use Froxlor\Cli\ConfigServices;
use Froxlor\Cli\PhpSessionclean;
use Froxlor\Cli\SwitchServerIp;
@@ -46,6 +47,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
require dirname(__DIR__) . '/lib/tables.inc.php';
$application = new Application('froxlor-cli', Froxlor::getFullVersion());
$application->add(new RunApiCommand());
$application->add(new ConfigServices());
$application->add(new PhpSessionclean());
$application->add(new SwitchServerIp());

View File

@@ -80,6 +80,8 @@ class Ajax
return $this->editApiKey();
case 'getConfigDetails':
return $this->getConfigDetails();
case 'getConfigJsonExport':
return $this->getConfigJsonExport();
default:
return $this->errorResponse('Action not found!');
}
@@ -324,4 +326,19 @@ class Ajax
}
return $this->errorResponse('Not allowed', 403);
}
/**
* download JSON export of config-selection
*/
private function getConfigJsonExport()
{
if (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1 && $this->userinfo['change_serversettings'] == 1) {
$params = $_GET;
unset($params['action']);
unset($params['finish']);
header('Content-disposition: attachment; filename=froxlor-config-' . time() . '.json');
return $this->jsonResponse($params);
}
return $this->errorResponse('Not allowed', 403);
}
}

View File

@@ -61,6 +61,12 @@ abstract class ApiParameter
*/
private function trimArray($input)
{
if ($input === '') {
return "";
}
if (is_numeric($input) || is_null($input)) {
return $input;
}
if (!is_array($input)) {
return trim($input);
}

View File

@@ -582,7 +582,7 @@ class Admins extends ApiCommand implements ResourceEntity
$idna_convert = new IdnaWrapper();
$email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true));
$def_language = Validate::validate($def_language, 'default language', '', '', [], true);
$custom_notes = Validate::validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', Validate::REGEX_CONF_TEXT, '', [], true);
$custom_notes = Validate::validate(str_replace("\r\n", "\n", $custom_notes ?? ""), 'custom_notes', Validate::REGEX_CONF_TEXT, '', [], true);
$theme = Validate::validate($theme, 'theme', '', '', [], true);
$password = Validate::validate($password, 'password', '', '', [], true);

View File

@@ -1451,6 +1451,7 @@ class Domains extends ApiCommand implements ResourceEntity
$ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']);
// check ssl IP
if (empty($p_ssl_ipandports) || (!is_array($p_ssl_ipandports) && is_null($p_ssl_ipandports))) {
$p_ssl_ipandports = [];
foreach ($result['ipsandports'] as $ip) {
if ($ip['ssl'] == 1) {
$p_ssl_ipandports[] = $ip['id'];

View File

@@ -84,7 +84,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
$id = $result['id'];
// current destination array
$result['destination_array'] = explode(' ', $result['destination']);
$result['destination_array'] = explode(' ', ($result['destination'] ?? ""));
// prepare destination
$destination = trim($destination);

View File

@@ -166,9 +166,11 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
$listen_statement = !empty($this->getBoolParam('listen_statement', true, 0)) ? 1 : 0;
$namevirtualhost_statement = !empty($this->getBoolParam('namevirtualhost_statement', true, 0)) ? 1 : 0;
$vhostcontainer = !empty($this->getBoolParam('vhostcontainer', true, 0)) ? 1 : 0;
$specialsettings = Validate::validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', true, '')), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$ss = $this->getParam('specialsettings', true, '');
$specialsettings = Validate::validate(str_replace("\r\n", "\n", $ss ?? ""), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$vhostcontainer_servername_statement = !empty($this->getBoolParam('vhostcontainer_servername_statement', true, 1)) ? 1 : 0;
$default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain', true, '')), 'default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$dvd = $this->getParam('default_vhostconf_domain', true, '');
$default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $dvd), 'default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$docroot = Validate::validate($this->getParam('docroot', true, ''), 'docroot', Validate::REGEX_DIR, '', [], true);
if ((int)Settings::Get('system.use_ssl') == 1) {
@@ -177,9 +179,11 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, ''), 'ssl_key_file', '', '', [], true);
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true);
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true);
$ssl_specialsettings = Validate::validate(str_replace("\r\n", "\n", $this->getParam('ssl_specialsettings', true, '')), 'ssl_specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$sslss = $this->getParam('ssl_specialsettings', true, '');
$ssl_specialsettings = Validate::validate(str_replace("\r\n", "\n", $sslss ?? ""), 'ssl_specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$include_specialsettings = !empty($this->getBoolParam('include_specialsettings', true, 0)) ? 1 : 0;
$ssl_default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $this->getParam('ssl_default_vhostconf_domain', true, '')), 'ssl_default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$ssldvd = $this->getParam('ssl_default_vhostconf_domain', true, '');
$ssl_default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $ssldvd ?? ""), 'ssl_default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$include_default_vhostconf_domain = !empty($this->getBoolParam('include_default_vhostconf_domain', true, 0)) ? 1 : 0;
} else {
$ssl = 0;
@@ -401,9 +405,11 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
$listen_statement = $this->getBoolParam('listen_statement', true, $result['listen_statement']);
$namevirtualhost_statement = $this->getBoolParam('namevirtualhost_statement', true, $result['namevirtualhost_statement']);
$vhostcontainer = $this->getBoolParam('vhostcontainer', true, $result['vhostcontainer']);
$specialsettings = Validate::validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', true, $result['specialsettings'])), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$ss = $this->getParam('specialsettings', true, $result['specialsettings']);
$specialsettings = Validate::validate(str_replace("\r\n", "\n", $ss ?? ""), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$vhostcontainer_servername_statement = $this->getParam('vhostcontainer_servername_statement', true, $result['vhostcontainer_servername_statement']);
$default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain', true, $result['default_vhostconf_domain'])), 'default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$dvd = $this->getParam('default_vhostconf_domain', true, $result['default_vhostconf_domain']);
$default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $dvd ?? ""), 'default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$docroot = Validate::validate($this->getParam('docroot', true, $result['docroot']), 'docroot', Validate::REGEX_DIR, '', [], true);
if ((int)Settings::Get('system.use_ssl') == 1) {
@@ -412,9 +418,11 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true);
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true);
$ssl_specialsettings = Validate::validate(str_replace("\r\n", "\n", $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings'])), 'ssl_specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);
$ssl_specialsettings = Validate::validate(str_replace("\r\n", "\n", $sslss ?? ""), 'ssl_specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);
$include_specialsettings = $this->getBoolParam('include_specialsettings', true, $result['include_specialsettings']);
$ssl_default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $this->getParam('ssl_default_vhostconf_domain', true, $result['ssl_default_vhostconf_domain'])), 'ssl_default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$ssldvd = $this->getParam('ssl_default_vhostconf_domain', true, $result['ssl_default_vhostconf_domain']);
$ssl_default_vhostconf_domain = Validate::validate(str_replace("\r\n", "\n", $ssldvd ?? ""), 'ssl_default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);
$include_default_vhostconf_domain = $this->getBoolParam('include_default_vhostconf_domain', true, $result['include_default_vhostconf_domain']);
} else {
$ssl = 0;

View File

@@ -0,0 +1,142 @@
<?php
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can also view it online at
* https://files.froxlor.org/misc/COPYING.txt
*
* @copyright the authors
* @author Froxlor team <team@froxlor.org>
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
namespace Froxlor\Cli;
use Exception;
use PDO;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Froxlor\Database\Database;
final class RunApiCommand extends CliCommand
{
protected function configure()
{
$this->setName('froxlor:api-call');
$this->setDescription('Run an API command as given user');
$this->addArgument('user', InputArgument::REQUIRED, 'Loginname of the user you want to run the command as')
->addArgument('api-command', InputArgument::REQUIRED, 'The command to execute in the form "Module.function"')
->addArgument('parameters', InputArgument::OPTIONAL, 'Paramaters to pass to the command as JSON array');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$result = self::SUCCESS;
$result = $this->validateRequirements($input, $output);
try {
$loginname = $input->getArgument('user');
$userinfo = $this->getUserByName($loginname);
$command = $input->getArgument('api-command');
$apicmd = $this->validateCommand($command);
$module = "\\Froxlor\\Api\\Commands\\" . $apicmd['class'];
$function = $apicmd['function'];
$params_json = $input->getArgument('parameters');
$params = json_decode($params_json ?? '', true);
$json_result = $module::getLocal($userinfo, $params)->{$function}();
$output->write($json_result);
$result = self::SUCCESS;
} catch (Exception $e) {
$output->writeln('<error>' . $e->getMessage() . '</>');
$result = self::FAILURE;
}
return $result;
}
private function validateCommand(string $command): array
{
$command = explode(".", $command);
if (count($command) != 2) {
throw new Exception("The given command is invalid.");
}
// simply check for file-existance, as we do not want to use our autoloader because this way
// it will recognize non-api classes+methods as valid commands
$apiclass = '\\Froxlor\\Api\\Commands\\' . $command[0];
if (!class_exists($apiclass) || !@method_exists($apiclass, $command[1])) {
throw new Exception("Unknown command");
}
return ['class' => $command[0], 'function' => $command[1]];
}
private function getUserByName(?string $loginname): array
{
if (empty($loginname)) {
throw new Exception("Empty username");
}
$stmt = Database::prepare("
SELECT `loginname` AS `customer`
FROM `" . TABLE_PANEL_CUSTOMERS . "`
WHERE `loginname`= :loginname
");
Database::pexecute($stmt, [
"loginname" => $loginname
]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row && $row['customer'] == $loginname) {
$table = "`" . TABLE_PANEL_CUSTOMERS . "`";
$adminsession = '0';
} else {
$stmt = Database::prepare("
SELECT `loginname` AS `admin` FROM `" . TABLE_PANEL_ADMINS . "`
WHERE `loginname`= :loginname
");
Database::pexecute($stmt, [
"loginname" => $loginname
]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row && $row['admin'] == $loginname) {
$table = "`" . TABLE_PANEL_ADMINS . "`";
$adminsession = '1';
} else {
throw new Exception("Unknown user '" . $loginname . "'");
}
}
$userinfo_stmt = Database::prepare("
SELECT * FROM $table
WHERE `loginname`= :loginname
");
Database::pexecute($userinfo_stmt, [
"loginname" => $loginname
]);
$userinfo = $userinfo_stmt->fetch(PDO::FETCH_ASSOC);
$userinfo['adminsession'] = $adminsession;
if ($userinfo['deactivated']) {
throw new Exception("User '" . $loginname . "' is currently deactivated");
}
return $userinfo;
}
}

View File

@@ -27,7 +27,7 @@ namespace Froxlor\Cron;
use ReflectionClass;
class TaskId
final class TaskId
{
/**
* TYPE=1 MEANS TO REBUILD APACHE VHOSTS.CONF

View File

@@ -138,7 +138,7 @@ class DbManagerMySQL
*/
public function deleteDatabase($dbname = null)
{
if (Database::getAttribute(PDO::ATTR_SERVER_VERSION) < '5.0.2') {
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) {
// failsafe if user has been deleted manually (requires MySQL 4.1.2+)
$stmt = Database::prepare("REVOKE ALL PRIVILEGES, GRANT OPTION FROM `" . $dbname . "`");
Database::pexecute($stmt, [], false);

View File

@@ -104,7 +104,9 @@
{{ lng('admin.configfiles.recommendednote') }}
</div>
<div class="col-12 col-md-6 text-end">
<input type="hidden" name="dist" value="{{ distribution }}" />
<button type="button" class="btn btn-outline-secondary" id="selectRecommendedConfig">{{ lng('admin.configfiles.selectrecommended') }}</button>
<button type="button" class="btn btn-outline-secondary" id="downloadSelectionAsJson"><i class="fa-solid fa-download"></i> {{ lng('admin.configfiles.downloadselected') }}</button>
<button type="submit" class="btn btn-primary">{{ lng('update.proceed') }}</button>
</div>
</div>

View File

@@ -12,6 +12,18 @@ $(function () {
})
});
/*
* export/download JSON file (e.g. for usage with config-services)
*/
$('#downloadSelectionAsJson').on('click', function () {
var formData = $(this).closest('form').serialize();
window.location = "lib/ajax.php?action=getConfigJsonExport&" + formData;
});
/*
* open modal window to show selected config-commands/files
* for selected daemon
*/
$('.show-config').on('click', function () {
var distro = $(this).data('dist');
var section = $(this).data('section');

View File

@@ -48,19 +48,6 @@ class TaskIDTest extends TestCase
$this->assertFalse($isNegativeValid, "Negative task should be invalid");
}
public function testAcceptNewTaskId()
{
$isTESTTASKValid = TaskIdExtended::isValid(10101010);
$this->assertTrue($isTESTTASKValid);
}
public function testFixedTaskIdTable()
{
$isTESTTASKValid = TaskIdExtended::isValid(10101010);
$this->assertTrue($isTESTTASKValid);
}
public function testIdMappingCorrect() {
foreach($this->fixedids as $name => $expected) {
$result = constant("\Froxlor\Cron\TaskId::$name");
@@ -78,7 +65,3 @@ class TaskIDTest extends TestCase
$this->assertFalse($unknownIDResult);
}
}
class TaskIdExtended extends TaskId {
const TESTTASK = 10101010;
}