Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c236d9eaab | ||
|
|
688994e40c | ||
|
|
9facaee809 | ||
|
|
a7dd5f4685 | ||
|
|
da810ea953 | ||
|
|
51b6e067e8 | ||
|
|
34cf6698bc | ||
|
|
4642160724 | ||
|
|
78a259ef3b | ||
|
|
68cf4ab69a | ||
|
|
d5661d492d | ||
|
|
6900898ae1 | ||
|
|
d90fb7fa68 | ||
|
|
4ea8629fcc | ||
|
|
9d4ff8698d |
@@ -28,7 +28,7 @@ require __DIR__ . '/lib/init.php';
|
||||
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\Http\HttpClient;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Install\AutoUpdate;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
@@ -132,7 +132,7 @@ elseif ($page == 'getdownload') {
|
||||
elseif ($page == 'extract') {
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
$toExtract = isset($_POST['archive']) ? $_POST['archive'] : null;
|
||||
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract;
|
||||
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir());
|
||||
$result = AutoUpdate::extractZip($localArchive);
|
||||
if ($result > 0) {
|
||||
@@ -146,7 +146,7 @@ elseif ($page == 'extract') {
|
||||
Response::redirectTo('admin_updates.php');
|
||||
} else {
|
||||
$toExtract = isset($_GET['archive']) ? $_GET['archive'] : null;
|
||||
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract;
|
||||
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
|
||||
}
|
||||
|
||||
if (!file_exists($localArchive)) {
|
||||
|
||||
61
index.php
61
index.php
@@ -53,9 +53,15 @@ if ($action == '2fa_entercode') {
|
||||
Response::redirectTo('index.php');
|
||||
exit();
|
||||
}
|
||||
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0;
|
||||
$message = "";
|
||||
if ($smessage > 0) {
|
||||
$message = lng('error.2fa_wrongcode');
|
||||
}
|
||||
// show template to enter code
|
||||
UI::view('login/enter2fa.html.twig', [
|
||||
'pagetitle' => lng('login.2fa')
|
||||
'pagetitle' => lng('login.2fa'),
|
||||
'message' => $message
|
||||
]);
|
||||
} elseif ($action == '2fa_verify') {
|
||||
// verify code from 2fa code-enter form
|
||||
@@ -68,25 +74,25 @@ if ($action == '2fa_entercode') {
|
||||
// verify entered code
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
|
||||
// get user-data
|
||||
$table = $_SESSION['uidtable_2fa'];
|
||||
$field = $_SESSION['uidfield_2fa'];
|
||||
$uid = $_SESSION['uid_2fa'];
|
||||
$isadmin = $_SESSION['unfo_2fa'];
|
||||
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code
|
||||
// which is temporarily stored for the customer when using email-2fa
|
||||
if ($result) {
|
||||
// get user-data
|
||||
$table = $_SESSION['uidtable_2fa'];
|
||||
$field = $_SESSION['uidfield_2fa'];
|
||||
$uid = $_SESSION['uid_2fa'];
|
||||
$isadmin = $_SESSION['unfo_2fa'];
|
||||
$sel_param = [
|
||||
'uid' => $uid
|
||||
];
|
||||
if ($_SESSION['secret_2fa'] == 'email') {
|
||||
// verify code by selecting user by id and the temp. stored code,
|
||||
// so only if it's the correct code, we get the user-data
|
||||
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
|
||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
|
||||
$sel_param['code'] = $code;
|
||||
} else {
|
||||
// Authenticator-verification has already happened at this point, so just get the user-data
|
||||
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid");
|
||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
|
||||
}
|
||||
$userinfo = Database::pexecute_first($sel_stmt, $sel_param);
|
||||
// whoops, no (valid) user? Start again
|
||||
@@ -108,15 +114,50 @@ if ($action == '2fa_entercode') {
|
||||
|
||||
// when using email-2fa, remove the one-time-code
|
||||
if ($userinfo['type_2fa'] == '1') {
|
||||
$del_stmt = Database::prepare("UPDATE $table SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
|
||||
$del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
|
||||
$userinfo = Database::pexecute_first($del_stmt, [
|
||||
'uid' => $uid
|
||||
]);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
// wrong 2fa code - treat like "wrong password"
|
||||
$stmt = Database::prepare("
|
||||
UPDATE " . $table . "
|
||||
SET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1
|
||||
WHERE `" . $field . "`= :uid
|
||||
");
|
||||
Database::pexecute($stmt, [
|
||||
"lastlogin_fail" => time(),
|
||||
"uid" => $uid
|
||||
]);
|
||||
|
||||
// get data for processing further
|
||||
$stmt = Database::prepare("
|
||||
SELECT `loginname`, `loginfail_count`, `lastlogin_fail` FROM " . $table . "
|
||||
WHERE `" . $field . "`= :uid
|
||||
");
|
||||
$fail_user = Database::pexecute_first($stmt, [
|
||||
"uid" => $uid
|
||||
]);
|
||||
|
||||
if ($fail_user['loginfail_count'] >= Settings::Get('login.maxloginattempts') && $fail_user['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))) {
|
||||
// Log failed login
|
||||
$rstlog = FroxlorLogger::getInstanceOf([
|
||||
'loginname' => $_SERVER['REMOTE_ADDR']
|
||||
]);
|
||||
$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "User '" . $fail_user['loginname'] . "' entered wrong 2fa code too often.");
|
||||
unset($fail_user);
|
||||
Response::redirectTo('index.php', [
|
||||
'showmessage' => '3'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
unset($fail_user);
|
||||
// back to form
|
||||
Response::redirectTo('index.php', [
|
||||
'showmessage' => '2'
|
||||
'action' => '2fa_entercode',
|
||||
'showmessage' => '1'
|
||||
]);
|
||||
exit();
|
||||
} elseif ($action == 'login') {
|
||||
|
||||
@@ -697,7 +697,7 @@ opcache.validate_timestamps'),
|
||||
('system', 'distribution', ''),
|
||||
('system', 'update_channel', 'stable'),
|
||||
('system', 'updatecheck_data', ''),
|
||||
('system', 'update_notify_last', '2.0.16'),
|
||||
('system', 'update_notify_last', '2.0.20'),
|
||||
('system', 'traffictool', 'goaccess'),
|
||||
('system', 'req_limit_per_interval', 60),
|
||||
('system', 'req_limit_interval', 60),
|
||||
@@ -744,7 +744,7 @@ opcache.validate_timestamps'),
|
||||
('panel', 'logo_overridetheme', '0'),
|
||||
('panel', 'logo_overridecustom', '0'),
|
||||
('panel', 'settings_mode', '0'),
|
||||
('panel', 'version', '2.0.16'),
|
||||
('panel', 'version', '2.0.20'),
|
||||
('panel', 'db_version', '202304260');
|
||||
|
||||
|
||||
|
||||
@@ -477,3 +477,23 @@ if (Froxlor::isFroxlorVersion('2.0.15')) {
|
||||
Update::showUpdateStep("Updating from 2.0.15 to 2.0.16", false);
|
||||
Froxlor::updateToVersion('2.0.16');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.16')) {
|
||||
Update::showUpdateStep("Updating from 2.0.16 to 2.0.17", false);
|
||||
Froxlor::updateToVersion('2.0.17');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.17')) {
|
||||
Update::showUpdateStep("Updating from 2.0.17 to 2.0.18", false);
|
||||
Froxlor::updateToVersion('2.0.18');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.18')) {
|
||||
Update::showUpdateStep("Updating from 2.0.18 to 2.0.19", false);
|
||||
Froxlor::updateToVersion('2.0.19');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.19')) {
|
||||
Update::showUpdateStep("Updating from 2.0.19 to 2.0.20", false);
|
||||
Froxlor::updateToVersion('2.0.20');
|
||||
}
|
||||
|
||||
@@ -99,6 +99,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
Response::standardError('notallowedtouseaccounts', '', true);
|
||||
}
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
// get email address
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
@@ -357,6 +362,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
$ea_optional = $id > 0;
|
||||
$emailaddr = $this->getParam('emailaddr', $ea_optional, '');
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
// validation
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
|
||||
@@ -77,6 +77,11 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$destination = $idna_convert->encode($destination);
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
'emailaddr' => $emailaddr
|
||||
|
||||
@@ -1171,7 +1171,6 @@ class Nginx extends HttpConfigBase
|
||||
$phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
|
||||
$phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
|
||||
$phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n";
|
||||
$phpopts .= "\t\ttry_files \$fastcgi_script_name =404;\n";
|
||||
$phpopts .= "\t\tfastcgi_pass " . Settings::Get('system.nginx_php_backend') . ";\n";
|
||||
$phpopts .= "\t\tfastcgi_index index.php;\n";
|
||||
if ($domain['ssl'] == '1' && $ssl_vhost) {
|
||||
|
||||
@@ -31,7 +31,7 @@ final class Froxlor
|
||||
{
|
||||
|
||||
// Main version variable
|
||||
const VERSION = '2.0.16';
|
||||
const VERSION = '2.0.20';
|
||||
|
||||
// Database version (YYYYMMDDC where C is a daily counter)
|
||||
const DBVERSION = '202304260';
|
||||
|
||||
@@ -17,8 +17,8 @@ class RateLimiter
|
||||
self::$reset_time = time() + 60;
|
||||
|
||||
if (!$install_mode) {
|
||||
self::$limit_per_interval = Settings::Get('system.req_limit_per_interval');
|
||||
self::$reset_time = time() + Settings::Get('system.req_limit_interval');
|
||||
self::$limit_per_interval = Settings::Get('system.req_limit_per_interval') ?? 60;
|
||||
self::$reset_time = time() + Settings::Get('system.req_limit_interval') ?? 60;
|
||||
}
|
||||
|
||||
// Get the remaining requests and reset time from the headers
|
||||
|
||||
@@ -42,7 +42,7 @@ class Install
|
||||
public $phpVersion;
|
||||
public $formfield;
|
||||
public string $requiredVersion = '7.4.0';
|
||||
public array $requiredExtensions = ['session', 'ctype', 'mysql', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json', 'gd'];
|
||||
public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json', 'gd'];
|
||||
public array $suggestedExtensions = ['bcmath', 'zip'];
|
||||
public array $suggestions = [];
|
||||
public array $criticals = [];
|
||||
|
||||
@@ -87,16 +87,38 @@ class UI
|
||||
|
||||
return $isHttps && (strcasecmp('on', $isHttps) == 0 || strcasecmp('https', $isHttps) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the cookie host from HTTP_HOST, stripping the port.
|
||||
*/
|
||||
public static function getCookieHost(): ?string
|
||||
{
|
||||
if (empty($_SERVER['HTTP_HOST']))
|
||||
return null;
|
||||
|
||||
$colonPosition = strrpos($_SERVER['HTTP_HOST'], ':');
|
||||
// There's no port in the host
|
||||
if ($colonPosition === false)
|
||||
return $_SERVER['HTTP_HOST'];
|
||||
|
||||
$closingSquareBracketPosition = strrpos($_SERVER['HTTP_HOST'], ']');
|
||||
// The host is an IPv4 address or hostname with port
|
||||
if ($closingSquareBracketPosition === false)
|
||||
return substr($_SERVER['HTTP_HOST'], 0, $colonPosition);
|
||||
|
||||
// The host is an IPv6 address with port
|
||||
return substr($_SERVER['HTTP_HOST'], 0, $closingSquareBracketPosition + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* send various security related headers
|
||||
*/
|
||||
public static function sendHeaders()
|
||||
{
|
||||
$cookie_host = empty($_SERVER['HTTP_HOST']) ? null : explode (':', $_SERVER['HTTP_HOST'])[0];
|
||||
session_set_cookie_params([
|
||||
'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php
|
||||
'path' => '/',
|
||||
'domain' => $cookie_host,
|
||||
'domain' => self::getCookieHost(),
|
||||
'secure' => self::requestIsHttps(),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
|
||||
@@ -52,16 +52,16 @@ class Check
|
||||
];
|
||||
|
||||
$check_array = [
|
||||
'system_mod_fcgid_enabled' => [
|
||||
'other_post_field' => 'system_phpfpm_enabled',
|
||||
'system_mod_fcgid' => [
|
||||
'other_post_field' => 'phpfpm_enabled',
|
||||
'other_enabled' => 'phpfpm.enabled',
|
||||
'other_enabled_lng' => 'phpfpmstillenabled',
|
||||
'deactivate' => [
|
||||
'phpfpm.enabled_ownvhost' => 0
|
||||
]
|
||||
],
|
||||
'system_phpfpm_enabled' => [
|
||||
'other_post_field' => 'system_mod_fcgid_enabled',
|
||||
'phpfpm_enabled' => [
|
||||
'other_post_field' => 'system_mod_fcgid',
|
||||
'other_enabled' => 'system.mod_fcgid',
|
||||
'other_enabled_lng' => 'fcgidstillenabled',
|
||||
'deactivate' => [
|
||||
|
||||
@@ -133,7 +133,7 @@ return [
|
||||
'customers' => [
|
||||
'label' => lng('admin.customers'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['customers'],
|
||||
'value' => empty($result['customers']) ? '0' : $result['customers'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
@@ -146,7 +146,7 @@ return [
|
||||
'domains' => [
|
||||
'label' => lng('admin.domains'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['domains'],
|
||||
'value' => empty($result['domains']) ? '0' : $result['domains'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
@@ -159,49 +159,49 @@ return [
|
||||
'diskspace' => [
|
||||
'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',
|
||||
'type' => 'textul',
|
||||
'value' => $result['diskspace'],
|
||||
'value' => empty($result['diskspace']) ? '0' : $result['diskspace'],
|
||||
'maxlength' => 6,
|
||||
'mandatory' => true
|
||||
],
|
||||
'traffic' => [
|
||||
'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',
|
||||
'type' => 'textul',
|
||||
'value' => $result['traffic'],
|
||||
'value' => empty($result['traffic']) ? '0' : $result['traffic'],
|
||||
'maxlength' => 4,
|
||||
'mandatory' => true
|
||||
],
|
||||
'subdomains' => [
|
||||
'label' => lng('customer.subdomains'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['subdomains'],
|
||||
'value' => empty($result['subdomains']) ? '0' : $result['subdomains'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'emails' => [
|
||||
'label' => lng('customer.emails'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['emails'],
|
||||
'value' => empty($result['emails']) ? '0' : $result['emails'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'email_accounts' => [
|
||||
'label' => lng('customer.accounts'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['email_accounts'],
|
||||
'value' => empty($result['email_accounts']) ? '0' : $result['email_accounts'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'email_forwarders' => [
|
||||
'label' => lng('customer.forwarders'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['email_forwarders'],
|
||||
'value' => empty($result['email_forwarders']) ? '0' : $result['email_forwarders'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'email_quota' => [
|
||||
'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',
|
||||
'type' => 'textul',
|
||||
'value' => $result['email_quota'],
|
||||
'value' => empty($result['email_quota']) ? '0' : $result['email_quota'],
|
||||
'maxlength' => 9,
|
||||
'visible' => Settings::Get('system.mail_quota_enabled') == '1',
|
||||
'mandatory' => true
|
||||
@@ -209,13 +209,13 @@ return [
|
||||
'ftps' => [
|
||||
'label' => lng('customer.ftps'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['ftps'],
|
||||
'value' => empty($result['ftps']) ? '0' : $result['ftps'],
|
||||
'maxlength' => 9
|
||||
],
|
||||
'mysqls' => [
|
||||
'label' => lng('customer.mysqls'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['mysqls'],
|
||||
'value' => empty($result['mysqls']) ? '0' : $result['mysqls'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
]
|
||||
|
||||
@@ -43,13 +43,16 @@ return [
|
||||
'email_password' => [
|
||||
'label' => lng('login.password'),
|
||||
'type' => 'password',
|
||||
'autocomplete' => 'off'
|
||||
],
|
||||
'email_password_suggestion' => [
|
||||
'label' => lng('customer.generated_pwd'),
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword()
|
||||
'autocomplete' => 'off',
|
||||
'next_to' => [
|
||||
'email_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -46,7 +46,7 @@ return [
|
||||
'autocomplete' => 'off',
|
||||
'mandatory' => true,
|
||||
'next_to' => [
|
||||
'admin_password_suggestion' => [
|
||||
'email_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
|
||||
@@ -331,11 +331,10 @@ if (CurrentUser::hasSession()) {
|
||||
}
|
||||
}
|
||||
// update cookie lifetime
|
||||
$cookie_host = empty($_SERVER['HTTP_HOST']) ? null : explode (':', $_SERVER['HTTP_HOST'])[0];
|
||||
$cookie_params = [
|
||||
'expires' => time() + Settings::Get('session.sessiontimeout'),
|
||||
'path' => '/',
|
||||
'domain' => $cookie_host,
|
||||
'domain' => UI::getCookieHost(),
|
||||
'secure' => UI::requestIsHttps(),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
|
||||
@@ -926,6 +926,7 @@ return [
|
||||
'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten',
|
||||
'invalidcronjobintervalvalue' => 'Cronjob Intervall muss einer der folgenden Werte sein: %s',
|
||||
'phpgdextensionnotavailable' => 'Die PHP GD Extension ist nicht verfügbar. Bild-Daten können nicht validiert werden.',
|
||||
'2fa_wrongcode' => 'Der angegebene Code ist nicht korrekt',
|
||||
],
|
||||
'extras' => [
|
||||
'description' => 'Hier können Sie zusätzliche Extras einrichten, wie zum Beispiel einen Verzeichnisschutz.<br />Die Änderungen sind erst nach einer kurzen Zeit wirksam.',
|
||||
|
||||
@@ -995,6 +995,7 @@ return [
|
||||
'domaincannotbeedited' => 'You are not permitted to edit the domain %s',
|
||||
'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %s',
|
||||
'phpgdextensionnotavailable' => 'The PHP GD extension is not available. Unable to validate image-data',
|
||||
'2fa_wrongcode' => 'The code entered is not valid',
|
||||
],
|
||||
'extras' => [
|
||||
'description' => 'Here you can add some extras, for example directory protection.<br />The system will need some time to apply the new settings after every change.',
|
||||
@@ -1193,7 +1194,7 @@ Yours sincerely, your administrator',
|
||||
'database_edit' => 'Edit database',
|
||||
'size' => 'Size',
|
||||
'privileged_user' => 'Privileged database user',
|
||||
'privileged_passwd' => 'Password for priviliged user',
|
||||
'privileged_passwd' => 'Password for privileged user',
|
||||
'unprivileged_passwd' => 'Password for unprivileged user',
|
||||
'mysql_ssl_ca_file' => 'SSL server certificate',
|
||||
'mysql_ssl_verify_server_certificate' => 'Verify SSL server certificate'
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ pagetitle }}</h5>
|
||||
|
||||
{% if message is not empty %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('error.error') }}</h4>
|
||||
<p>{{ message|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="2fa_code" class="col-form-label">{{ lng('login.2facode') }}</label>
|
||||
<input class="form-control" type="text" name="2fa_code" id="2fa_code" value="" autocomplete="off" autofocus required/>
|
||||
|
||||
Reference in New Issue
Block a user