Compare commits

...

13 Commits

Author SHA1 Message Date
Michael Kaufmann
c236d9eaab set version to 2.0.20 for upcoming release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-06-02 20:13:36 +02:00
Michael Kaufmann
688994e40c idna encode umlaut-emailaddresses when adding email-forwarder
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-29 20:52:57 +02:00
Michael Kaufmann
9facaee809 re-enable fcgid/php-fpm activation-validate-check
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-28 15:49:06 +02:00
Michael Kaufmann
a7dd5f4685 show 0 value of resource-fields if value is empty, fixes #1149
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-28 10:46:28 +02:00
Michael Kaufmann
da810ea953 secure filename of local-archive in webupdate
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-25 09:51:55 +02:00
Michael Kaufmann
51b6e067e8 idna encode umlaut-emailaddresses when adding/editing email-account; use correct password-suggestion-layout in change-email-account formfield
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-25 08:26:39 +02:00
Michael Kaufmann
34cf6698bc remove superfluous try_files in nginx config if php-backend (non-fastcgi) is used
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-15 20:14:26 +02:00
Michael Kaufmann
4642160724 add same loginfail restrictions for entering 2fa code as for user/pwd login
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-12 10:36:27 +02:00
Nicolas Thumann
78a259ef3b Fix IPv6 address in cookie domain (#1137)
* Implement getCookieHost to extract cookie host from HTTP_HOST
2023-05-10 08:26:08 +02:00
Nicolas Thumann
68cf4ab69a Fix typo in English privileged_passwd (#1136) 2023-05-09 18:52:43 +02:00
Michael Kaufmann
d5661d492d set version to 2.0.19 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 11:07:31 +02:00
Michael Kaufmann
6900898ae1 typo in updater
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 11:03:21 +02:00
Michael Kaufmann
d90fb7fa68 fix mysql-pdo check on installation, set version to 2.0.18 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 10:54:47 +02:00
18 changed files with 149 additions and 46 deletions

View File

@@ -28,7 +28,7 @@ require __DIR__ . '/lib/init.php';
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Http\HttpClient; use Froxlor\FileDir;
use Froxlor\Install\AutoUpdate; use Froxlor\Install\AutoUpdate;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
@@ -132,7 +132,7 @@ elseif ($page == 'getdownload') {
elseif ($page == 'extract') { elseif ($page == 'extract') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (isset($_POST['send']) && $_POST['send'] == 'send') {
$toExtract = isset($_POST['archive']) ? $_POST['archive'] : null; $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()); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir());
$result = AutoUpdate::extractZip($localArchive); $result = AutoUpdate::extractZip($localArchive);
if ($result > 0) { if ($result > 0) {
@@ -146,7 +146,7 @@ elseif ($page == 'extract') {
Response::redirectTo('admin_updates.php'); Response::redirectTo('admin_updates.php');
} else { } else {
$toExtract = isset($_GET['archive']) ? $_GET['archive'] : null; $toExtract = isset($_GET['archive']) ? $_GET['archive'] : null;
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract; $localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
} }
if (!file_exists($localArchive)) { if (!file_exists($localArchive)) {

View File

@@ -53,9 +53,15 @@ if ($action == '2fa_entercode') {
Response::redirectTo('index.php'); Response::redirectTo('index.php');
exit(); exit();
} }
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0;
$message = "";
if ($smessage > 0) {
$message = lng('error.2fa_wrongcode');
}
// show template to enter code // show template to enter code
UI::view('login/enter2fa.html.twig', [ UI::view('login/enter2fa.html.twig', [
'pagetitle' => lng('login.2fa') 'pagetitle' => lng('login.2fa'),
'message' => $message
]); ]);
} elseif ($action == '2fa_verify') { } elseif ($action == '2fa_verify') {
// verify code from 2fa code-enter form // verify code from 2fa code-enter form
@@ -68,25 +74,25 @@ if ($action == '2fa_entercode') {
// verify entered code // verify entered code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname')); $tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3)); $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 // 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 // which is temporarily stored for the customer when using email-2fa
if ($result) { if ($result) {
// get user-data
$table = $_SESSION['uidtable_2fa'];
$field = $_SESSION['uidfield_2fa'];
$uid = $_SESSION['uid_2fa'];
$isadmin = $_SESSION['unfo_2fa'];
$sel_param = [ $sel_param = [
'uid' => $uid 'uid' => $uid
]; ];
if ($_SESSION['secret_2fa'] == 'email') { if ($_SESSION['secret_2fa'] == 'email') {
// verify code by selecting user by id and the temp. stored code, // 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 // 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; $sel_param['code'] = $code;
} else { } else {
// Authenticator-verification has already happened at this point, so just get the user-data // 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); $userinfo = Database::pexecute_first($sel_stmt, $sel_param);
// whoops, no (valid) user? Start again // whoops, no (valid) user? Start again
@@ -108,15 +114,50 @@ if ($action == '2fa_entercode') {
// when using email-2fa, remove the one-time-code // when using email-2fa, remove the one-time-code
if ($userinfo['type_2fa'] == '1') { 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, [ $userinfo = Database::pexecute_first($del_stmt, [
'uid' => $uid 'uid' => $uid
]); ]);
} }
exit(); 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', [ Response::redirectTo('index.php', [
'showmessage' => '2' 'action' => '2fa_entercode',
'showmessage' => '1'
]); ]);
exit(); exit();
} elseif ($action == 'login') { } elseif ($action == 'login') {

View File

@@ -697,7 +697,7 @@ opcache.validate_timestamps'),
('system', 'distribution', ''), ('system', 'distribution', ''),
('system', 'update_channel', 'stable'), ('system', 'update_channel', 'stable'),
('system', 'updatecheck_data', ''), ('system', 'updatecheck_data', ''),
('system', 'update_notify_last', '2.0.17'), ('system', 'update_notify_last', '2.0.20'),
('system', 'traffictool', 'goaccess'), ('system', 'traffictool', 'goaccess'),
('system', 'req_limit_per_interval', 60), ('system', 'req_limit_per_interval', 60),
('system', 'req_limit_interval', 60), ('system', 'req_limit_interval', 60),
@@ -744,7 +744,7 @@ opcache.validate_timestamps'),
('panel', 'logo_overridetheme', '0'), ('panel', 'logo_overridetheme', '0'),
('panel', 'logo_overridecustom', '0'), ('panel', 'logo_overridecustom', '0'),
('panel', 'settings_mode', '0'), ('panel', 'settings_mode', '0'),
('panel', 'version', '2.0.17'), ('panel', 'version', '2.0.20'),
('panel', 'db_version', '202304260'); ('panel', 'db_version', '202304260');

View File

@@ -482,3 +482,18 @@ if (Froxlor::isFroxlorVersion('2.0.16')) {
Update::showUpdateStep("Updating from 2.0.16 to 2.0.17", false); Update::showUpdateStep("Updating from 2.0.16 to 2.0.17", false);
Froxlor::updateToVersion('2.0.17'); 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');
}

View File

@@ -99,6 +99,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
Response::standardError('notallowedtouseaccounts', '', true); Response::standardError('notallowedtouseaccounts', '', true);
} }
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// get email address // get email address
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
@@ -357,6 +362,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$ea_optional = $id > 0; $ea_optional = $id > 0;
$emailaddr = $this->getParam('emailaddr', $ea_optional, ''); $emailaddr = $this->getParam('emailaddr', $ea_optional, '');
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// validation // validation
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,

View File

@@ -77,6 +77,11 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
$destination = $idna_convert->encode($destination); $destination = $idna_convert->encode($destination);
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
'emailaddr' => $emailaddr 'emailaddr' => $emailaddr

View File

@@ -1171,7 +1171,6 @@ class Nginx extends HttpConfigBase
$phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n"; $phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
$phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n"; $phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
$phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\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_pass " . Settings::Get('system.nginx_php_backend') . ";\n";
$phpopts .= "\t\tfastcgi_index index.php;\n"; $phpopts .= "\t\tfastcgi_index index.php;\n";
if ($domain['ssl'] == '1' && $ssl_vhost) { if ($domain['ssl'] == '1' && $ssl_vhost) {

View File

@@ -31,7 +31,7 @@ final class Froxlor
{ {
// Main version variable // Main version variable
const VERSION = '2.0.17'; const VERSION = '2.0.20';
// Database version (YYYYMMDDC where C is a daily counter) // Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '202304260'; const DBVERSION = '202304260';

View File

@@ -42,7 +42,7 @@ class Install
public $phpVersion; public $phpVersion;
public $formfield; public $formfield;
public string $requiredVersion = '7.4.0'; 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 $suggestedExtensions = ['bcmath', 'zip'];
public array $suggestions = []; public array $suggestions = [];
public array $criticals = []; public array $criticals = [];

View File

@@ -87,16 +87,38 @@ class UI
return $isHttps && (strcasecmp('on', $isHttps) == 0 || strcasecmp('https', $isHttps) == 0); 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 * send various security related headers
*/ */
public static function sendHeaders() public static function sendHeaders()
{ {
$cookie_host = empty($_SERVER['HTTP_HOST']) ? null : explode (':', $_SERVER['HTTP_HOST'])[0];
session_set_cookie_params([ session_set_cookie_params([
'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php 'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php
'path' => '/', 'path' => '/',
'domain' => $cookie_host, 'domain' => self::getCookieHost(),
'secure' => self::requestIsHttps(), 'secure' => self::requestIsHttps(),
'httponly' => true, 'httponly' => true,
'samesite' => 'Strict' 'samesite' => 'Strict'

View File

@@ -52,16 +52,16 @@ class Check
]; ];
$check_array = [ $check_array = [
'system_mod_fcgid_enabled' => [ 'system_mod_fcgid' => [
'other_post_field' => 'system_phpfpm_enabled', 'other_post_field' => 'phpfpm_enabled',
'other_enabled' => 'phpfpm.enabled', 'other_enabled' => 'phpfpm.enabled',
'other_enabled_lng' => 'phpfpmstillenabled', 'other_enabled_lng' => 'phpfpmstillenabled',
'deactivate' => [ 'deactivate' => [
'phpfpm.enabled_ownvhost' => 0 'phpfpm.enabled_ownvhost' => 0
] ]
], ],
'system_phpfpm_enabled' => [ 'phpfpm_enabled' => [
'other_post_field' => 'system_mod_fcgid_enabled', 'other_post_field' => 'system_mod_fcgid',
'other_enabled' => 'system.mod_fcgid', 'other_enabled' => 'system.mod_fcgid',
'other_enabled_lng' => 'fcgidstillenabled', 'other_enabled_lng' => 'fcgidstillenabled',
'deactivate' => [ 'deactivate' => [

View File

@@ -133,7 +133,7 @@ return [
'customers' => [ 'customers' => [
'label' => lng('admin.customers'), 'label' => lng('admin.customers'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['customers'], 'value' => empty($result['customers']) ? '0' : $result['customers'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
@@ -146,7 +146,7 @@ return [
'domains' => [ 'domains' => [
'label' => lng('admin.domains'), 'label' => lng('admin.domains'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['domains'], 'value' => empty($result['domains']) ? '0' : $result['domains'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
@@ -159,49 +159,49 @@ return [
'diskspace' => [ 'diskspace' => [
'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')', 'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['diskspace'], 'value' => empty($result['diskspace']) ? '0' : $result['diskspace'],
'maxlength' => 6, 'maxlength' => 6,
'mandatory' => true 'mandatory' => true
], ],
'traffic' => [ 'traffic' => [
'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')', 'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['traffic'], 'value' => empty($result['traffic']) ? '0' : $result['traffic'],
'maxlength' => 4, 'maxlength' => 4,
'mandatory' => true 'mandatory' => true
], ],
'subdomains' => [ 'subdomains' => [
'label' => lng('customer.subdomains'), 'label' => lng('customer.subdomains'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['subdomains'], 'value' => empty($result['subdomains']) ? '0' : $result['subdomains'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'emails' => [ 'emails' => [
'label' => lng('customer.emails'), 'label' => lng('customer.emails'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['emails'], 'value' => empty($result['emails']) ? '0' : $result['emails'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_accounts' => [ 'email_accounts' => [
'label' => lng('customer.accounts'), 'label' => lng('customer.accounts'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_accounts'], 'value' => empty($result['email_accounts']) ? '0' : $result['email_accounts'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_forwarders' => [ 'email_forwarders' => [
'label' => lng('customer.forwarders'), 'label' => lng('customer.forwarders'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_forwarders'], 'value' => empty($result['email_forwarders']) ? '0' : $result['email_forwarders'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_quota' => [ 'email_quota' => [
'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')', 'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_quota'], 'value' => empty($result['email_quota']) ? '0' : $result['email_quota'],
'maxlength' => 9, 'maxlength' => 9,
'visible' => Settings::Get('system.mail_quota_enabled') == '1', 'visible' => Settings::Get('system.mail_quota_enabled') == '1',
'mandatory' => true 'mandatory' => true
@@ -209,13 +209,13 @@ return [
'ftps' => [ 'ftps' => [
'label' => lng('customer.ftps'), 'label' => lng('customer.ftps'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['ftps'], 'value' => empty($result['ftps']) ? '0' : $result['ftps'],
'maxlength' => 9 'maxlength' => 9
], ],
'mysqls' => [ 'mysqls' => [
'label' => lng('customer.mysqls'), 'label' => lng('customer.mysqls'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['mysqls'], 'value' => empty($result['mysqls']) ? '0' : $result['mysqls'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
] ]

View File

@@ -43,13 +43,16 @@ return [
'email_password' => [ 'email_password' => [
'label' => lng('login.password'), 'label' => lng('login.password'),
'type' => 'password', 'type' => 'password',
'autocomplete' => 'off' 'autocomplete' => 'off',
], 'next_to' => [
'email_password_suggestion' => [ 'email_password_suggestion' => [
'label' => lng('customer.generated_pwd'), 'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text', 'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''), 'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword() 'value' => Crypt::generatePassword(),
'readonly' => true
]
]
] ]
] ]
] ]

View File

@@ -46,7 +46,7 @@ return [
'autocomplete' => 'off', 'autocomplete' => 'off',
'mandatory' => true, 'mandatory' => true,
'next_to' => [ 'next_to' => [
'admin_password_suggestion' => [ 'email_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':', 'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text', 'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''), 'visible' => (Settings::Get('panel.password_regex') == ''),

View File

@@ -331,11 +331,10 @@ if (CurrentUser::hasSession()) {
} }
} }
// update cookie lifetime // update cookie lifetime
$cookie_host = empty($_SERVER['HTTP_HOST']) ? null : explode (':', $_SERVER['HTTP_HOST'])[0];
$cookie_params = [ $cookie_params = [
'expires' => time() + Settings::Get('session.sessiontimeout'), 'expires' => time() + Settings::Get('session.sessiontimeout'),
'path' => '/', 'path' => '/',
'domain' => $cookie_host, 'domain' => UI::getCookieHost(),
'secure' => UI::requestIsHttps(), 'secure' => UI::requestIsHttps(),
'httponly' => true, 'httponly' => true,
'samesite' => 'Strict' 'samesite' => 'Strict'

View File

@@ -926,6 +926,7 @@ return [
'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten', 'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten',
'invalidcronjobintervalvalue' => 'Cronjob Intervall muss einer der folgenden Werte sein: %s', '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.', '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' => [ '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.', 'description' => 'Hier können Sie zusätzliche Extras einrichten, wie zum Beispiel einen Verzeichnisschutz.<br />Die Änderungen sind erst nach einer kurzen Zeit wirksam.',

View File

@@ -995,6 +995,7 @@ return [
'domaincannotbeedited' => 'You are not permitted to edit the domain %s', 'domaincannotbeedited' => 'You are not permitted to edit the domain %s',
'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %s', 'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %s',
'phpgdextensionnotavailable' => 'The PHP GD extension is not available. Unable to validate image-data', 'phpgdextensionnotavailable' => 'The PHP GD extension is not available. Unable to validate image-data',
'2fa_wrongcode' => 'The code entered is not valid',
], ],
'extras' => [ '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.', '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', 'database_edit' => 'Edit database',
'size' => 'Size', 'size' => 'Size',
'privileged_user' => 'Privileged database user', 'privileged_user' => 'Privileged database user',
'privileged_passwd' => 'Password for priviliged user', 'privileged_passwd' => 'Password for privileged user',
'unprivileged_passwd' => 'Password for unprivileged user', 'unprivileged_passwd' => 'Password for unprivileged user',
'mysql_ssl_ca_file' => 'SSL server certificate', 'mysql_ssl_ca_file' => 'SSL server certificate',
'mysql_ssl_verify_server_certificate' => 'Verify SSL server certificate' 'mysql_ssl_verify_server_certificate' => 'Verify SSL server certificate'

View File

@@ -10,6 +10,13 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{{ pagetitle }}</h5> <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"> <div class="mb-3">
<label for="2fa_code" class="col-form-label">{{ lng('login.2facode') }}</label> <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/> <input class="form-control" type="text" name="2fa_code" id="2fa_code" value="" autocomplete="off" autofocus required/>