diff --git a/index.php b/index.php
index 205b0517..dd3f2673 100644
--- a/index.php
+++ b/index.php
@@ -72,6 +72,7 @@ if ($action == '2fa_entercode') {
exit();
}
$code = Request::post('2fa_code');
+ $remember = Request::post('2fa_remember');
// verify entered code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
// get user-data
@@ -105,13 +106,6 @@ if ($action == '2fa_entercode') {
$userinfo['adminsession'] = $isadmin;
$userinfo['userid'] = $uid;
- // if not successful somehow - start again
- if (!finishLogin($userinfo)) {
- Response::redirectTo('index.php', [
- 'showmessage' => '2'
- ]);
- }
-
// 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");
@@ -119,6 +113,42 @@ if ($action == '2fa_entercode') {
'uid' => $uid
]);
}
+
+ // when remember is activated, set the cookie
+ if ($remember) {
+ $selector = base64_encode(Froxlor::genSessionId(9));
+ $authenticator = Froxlor::genSessionId(33);
+ $valid_until = time()+60*60*24*30;
+ $ins_stmt = Database::prepare("
+ INSERT INTO `".TABLE_PANEL_2FA_TOKENS."` SET
+ `selector` = :selector,
+ `token` = :authenticator,
+ `userid` = :userid,
+ `valid_until` = :valid_until
+ ");
+ Database::pexecute($ins_stmt, [
+ 'selector' => $selector,
+ 'authenticator' => hash('sha256', $authenticator),
+ 'userid' => $uid,
+ 'valid_until' => $valid_until
+ ]);
+ $cookie_params = [
+ 'expires' => $valid_until, // 30 days
+ 'path' => '/',
+ 'domain' => UI::getCookieHost(),
+ 'secure' => UI::requestIsHttps(),
+ 'httponly' => true,
+ 'samesite' => 'Strict'
+ ];
+ setcookie('frx_2fa_remember', $selector.':'.base64_encode($authenticator), $cookie_params);
+ }
+
+ // if not successful somehow - start again
+ if (!finishLogin($userinfo)) {
+ Response::redirectTo('index.php', [
+ 'showmessage' => '2'
+ ]);
+ }
exit();
}
// wrong 2fa code - treat like "wrong password"
@@ -349,6 +379,22 @@ if ($action == '2fa_entercode') {
// 2FA activated
if (Settings::Get('2fa.enabled') == '1' && $userinfo['type_2fa'] > 0) {
+
+ // check for remember cookie
+ if (!empty($_COOKIE['frx_2fa_remember'])) {
+ list($selector, $authenticator) = explode(':', $_COOKIE['frx_2fa_remember']);
+ $sel_stmt = Database::prepare("SELECT `token` FROM `".TABLE_PANEL_2FA_TOKENS."` WHERE `selector` = :selector AND `userid` = :uid AND `valid_until` >= UNIX_TIMESTAMP()");
+ $token_check = Database::pexecute_first($sel_stmt, ['selector' => $selector, 'uid' => $userinfo[$uid]]);
+ if ($token_check && hash_equals($token_check['token'], hash('sha256', base64_decode($authenticator)))) {
+ if (!finishLogin($userinfo)) {
+ Response::redirectTo('index.php', [
+ 'showmessage' => '2'
+ ]);
+ }
+ exit();
+ }
+ }
+
// redirect to code-enter-page
$_SESSION['secret_2fa'] = ($userinfo['type_2fa'] == 2 ? $userinfo['data_2fa'] : 'email');
$_SESSION['uid_2fa'] = $userinfo[$uid];
@@ -829,8 +875,8 @@ function finishLogin($userinfo)
$theme = $userinfo['theme'];
} else {
$theme = Settings::Get('panel.default_theme');
- CurrentUser::setField('theme', $theme);
}
+ CurrentUser::setField('theme', $theme);
$qryparams = [];
if (!empty($_SESSION['lastqrystr'])) {
diff --git a/install/froxlor.sql.php b/install/froxlor.sql.php
index 299560de..5ffdb4a6 100644
--- a/install/froxlor.sql.php
+++ b/install/froxlor.sql.php
@@ -730,8 +730,8 @@ opcache.validate_timestamps'),
('panel', 'logo_overridecustom', '0'),
('panel', 'settings_mode', '0'),
('panel', 'menu_collapsed', '1'),
- ('panel', 'version', '2.2.0-rc1'),
- ('panel', 'db_version', '202401090');
+ ('panel', 'version', '2.2.0-rc2'),
+ ('panel', 'db_version', '202407200');
DROP TABLE IF EXISTS `panel_tasks`;
@@ -1049,4 +1049,15 @@ CREATE TABLE `panel_loginlinks` (
`allowed_from` text NOT NULL,
UNIQUE KEY `loginname` (`loginname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
+
+
+DROP TABLE IF EXISTS `panel_2fa_tokens`;
+CREATE TABLE `panel_2fa_tokens` (
+ `id` int(11) NOT NULL auto_increment,
+ `selector` varchar(20) NOT NULL,
+ `token` varchar(200) NOT NULL,
+ `userid` int(11) NOT NULL default '0',
+ `valid_until` int(15) NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
FROXLORSQL;
diff --git a/install/updates/froxlor/update_2.2.inc.php b/install/updates/froxlor/update_2.2.inc.php
index 9e96a98a..46645b5e 100644
--- a/install/updates/froxlor/update_2.2.inc.php
+++ b/install/updates/froxlor/update_2.2.inc.php
@@ -122,3 +122,26 @@ if (Froxlor::isFroxlorVersion('2.2.0-dev1')) {
Update::showUpdateStep("Updating from 2.2.0-dev1 to 2.2.0-rc1", false);
Froxlor::updateToVersion('2.2.0-rc1');
}
+
+if (Froxlor::isDatabaseVersion('202401090')) {
+
+ Update::showUpdateStep("Adding new table for 2fa tokens");
+ Database::query("DROP TABLE IF EXISTS `panel_2fa_tokens`;");
+ $sql = "CREATE TABLE `panel_2fa_tokens` (
+ `id` int(11) NOT NULL auto_increment,
+ `selector` varchar(20) NOT NULL,
+ `token` varchar(200) NOT NULL,
+ `userid` int(11) NOT NULL default '0',
+ `valid_until` int(15) NOT NULL,
+ PRIMARY KEY (id)
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;";
+ Database::query($sql);
+ Update::lastStepStatus(0);
+
+ Froxlor::updateToDbVersion('202407200');
+}
+
+if (Froxlor::isFroxlorVersion('2.2.0-rc1')) {
+ Update::showUpdateStep("Updating from 2.2.0-rc1 to 2.2.0-rc2", false);
+ Froxlor::updateToVersion('2.2.0-rc2');
+}
diff --git a/lib/Froxlor/Api/Commands/Emails.php b/lib/Froxlor/Api/Commands/Emails.php
index 60e81f59..c52d6dc5 100644
--- a/lib/Froxlor/Api/Commands/Emails.php
+++ b/lib/Froxlor/Api/Commands/Emails.php
@@ -270,15 +270,6 @@ class Emails extends ApiCommand implements ResourceEntity
throw new Exception("You cannot access this resource", 405);
}
- // if enabling catchall is not allowed by settings, we do not need
- // to run update()
- if (Settings::Get('catchall.catchall_enabled') != '1') {
- Response::standardError([
- 'operationnotpermitted',
- 'featureisdisabled'
- ], 'catchall', true);
- }
-
$id = $this->getParam('id', true, 0);
$ea_optional = $id > 0;
$emailaddr = $this->getParam('emailaddr', $ea_optional, '');
@@ -297,30 +288,41 @@ class Emails extends ApiCommand implements ResourceEntity
$iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']);
$description = $this->getParam('description', true, $result['description']);
+ // if enabling catchall is not allowed by settings, we do not need
+ // to run update()
+ if ($iscatchall && $result['iscatchall'] == 0 && Settings::Get('catchall.catchall_enabled') != '1') {
+ Response::standardError([
+ 'operationnotpermitted',
+ 'featureisdisabled'
+ ], 'catchall', true);
+ }
+
// get needed customer info to reduce the email-address-counter by one
$customer = $this->getCustomerData();
// check for catchall-flag
+ $email = $result['email_full'];
if ($iscatchall) {
$iscatchall = '1';
- $email_parts = explode('@', $result['email_full']);
- $email = '@' . $email_parts[1];
- // catchall check
- $stmt = Database::prepare("
- SELECT `email_full` FROM `" . TABLE_MAIL_VIRTUAL . "`
- WHERE `email` = :email AND `customerid` = :cid AND `iscatchall` = '1'
- ");
- $params = [
- "email" => $email,
- "cid" => $customer['customerid']
- ];
- $email_check = Database::pexecute_first($stmt, $params, true, true);
- if ($email_check) {
- Response::standardError('youhavealreadyacatchallforthisdomain', '', true);
+ $email = $result['email'];
+ // update only required if it was not a catchall before
+ if ($result['iscatchall'] == 0) {
+ $email_parts = explode('@', $result['email_full']);
+ $email = '@' . $email_parts[1];
+ // catchall check
+ $stmt = Database::prepare("
+ SELECT `email_full` FROM `" . TABLE_MAIL_VIRTUAL . "`
+ WHERE `email` = :email AND `customerid` = :cid AND `iscatchall` = '1'
+ ");
+ $params = [
+ "email" => $email,
+ "cid" => $customer['customerid']
+ ];
+ $email_check = Database::pexecute_first($stmt, $params, true, true);
+ if ($email_check) {
+ Response::standardError('youhavealreadyacatchallforthisdomain', '', true);
+ }
}
- } else {
- $iscatchall = '0';
- $email = $result['email_full'];
}
$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\d{1,}(\.\d{1,2})?$/', '', [7.0], true);
diff --git a/lib/Froxlor/Api/Commands/SubDomains.php b/lib/Froxlor/Api/Commands/SubDomains.php
index 6c796234..1c4870c2 100644
--- a/lib/Froxlor/Api/Commands/SubDomains.php
+++ b/lib/Froxlor/Api/Commands/SubDomains.php
@@ -983,9 +983,11 @@ class SubDomains extends ApiCommand implements ResourceEntity
'`d`.`letsencrypt`',
'`d`.`registration_date`',
'`d`.`termination_date`',
- '`d`.`deactivated`'
+ '`d`.`deactivated`',
+ '`d`.`email_only`',
];
}
+
$query_fields = [];
// prepare select statement
@@ -996,7 +998,6 @@ class SubDomains extends ApiCommand implements ResourceEntity
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `da` ON `da`.`aliasdomain`=`d`.`id`
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON `pd`.`id`=`d`.`parentdomainid`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
- AND `d`.`email_only` = '0'
" . $this->getSearchWhere($query_fields, true) . " GROUP BY `d`.`id` ORDER BY `parentdomainname` ASC, `d`.`parentdomainid` ASC " . $this->getOrderBy(true) . $this->getLimit());
$result = [];
@@ -1092,13 +1093,13 @@ class SubDomains extends ApiCommand implements ResourceEntity
$this->getUserDetail('customerid')
];
}
+
if (!empty($customer_ids)) {
// prepare select statement
$domains_stmt = Database::prepare("
SELECT COUNT(*) as num_subdom
FROM `" . TABLE_PANEL_DOMAINS . "` `d`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
- AND `d`.`email_only` = '0'
");
$result = Database::pexecute_first($domains_stmt, null, true, true);
if ($result) {
diff --git a/lib/Froxlor/Cli/MasterCron.php b/lib/Froxlor/Cli/MasterCron.php
index 61926ecc..7045de8e 100644
--- a/lib/Froxlor/Cli/MasterCron.php
+++ b/lib/Froxlor/Cli/MasterCron.php
@@ -171,8 +171,9 @@ final class MasterCron extends CliCommand
FroxlorLogger::getInstanceOf()->setCronLog(0);
}
- // clean up possible old login-links
+ // clean up possible old login-links and 2fa tokens
Database::query("DELETE FROM `" . TABLE_PANEL_LOGINLINKS . "` WHERE `valid_until` < UNIX_TIMESTAMP()");
+ Database::query("DELETE FROM `" . TABLE_PANEL_2FA_TOKENS . "` WHERE `valid_until` < UNIX_TIMESTAMP()");
return $result;
}
diff --git a/lib/Froxlor/Dns/Dns.php b/lib/Froxlor/Dns/Dns.php
index a8ca8771..b49a7e22 100644
--- a/lib/Froxlor/Dns/Dns.php
+++ b/lib/Froxlor/Dns/Dns.php
@@ -54,7 +54,7 @@ class Dns
$dom_data['uid'] = $userinfo['userid'];
}
} else {
- $where_clause = '`customerid` = :uid AND ';
+ $where_clause = '`customerid` = :uid AND `email_only` = "0" AND ';
$dom_data['uid'] = $userinfo['userid'];
}
diff --git a/lib/Froxlor/Froxlor.php b/lib/Froxlor/Froxlor.php
index 83bc5501..0a5011e3 100644
--- a/lib/Froxlor/Froxlor.php
+++ b/lib/Froxlor/Froxlor.php
@@ -31,10 +31,10 @@ final class Froxlor
{
// Main version variable
- const VERSION = '2.2.0-rc1';
+ const VERSION = '2.2.0-rc2';
// Database version (YYYYMMDDC where C is a daily counter)
- const DBVERSION = '202401090';
+ const DBVERSION = '202407200';
// Distribution branding-tag (used for Debian etc.)
const BRANDING = '';
diff --git a/lib/Froxlor/UI/Callbacks/Domain.php b/lib/Froxlor/UI/Callbacks/Domain.php
index b8fb3f68..f635ae25 100644
--- a/lib/Froxlor/UI/Callbacks/Domain.php
+++ b/lib/Froxlor/UI/Callbacks/Domain.php
@@ -74,6 +74,9 @@ class Domain
if ($attributes['fields']['deactivated']) {
return lng('admin.deactivated');
}
+ if ($attributes['fields']['email_only']) {
+ return lng('domains.email_only');
+ }
// path or redirect
if (preg_match('/^https?\:\/\//', $attributes['fields']['documentroot'])) {
return [
@@ -127,7 +130,7 @@ class Domain
public static function canViewLogs(array $attributes): bool
{
- if ((!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0)) && !$attributes['fields']['deactivated']) {
+ if ((int)$attributes['fields']['email_only'] == 0 && !$attributes['fields']['deactivated']) {
if ((int)UI::getCurrentUser()['adminsession'] == 0 && (bool)UI::getCurrentUser()['logviewenabled']) {
return true;
} elseif ((int)UI::getCurrentUser()['adminsession'] == 1) {
@@ -157,6 +160,7 @@ class Domain
&& $attributes['fields']['caneditdomain'] == '1'
&& Settings::Get('system.bind_enable') == '1'
&& Settings::Get('system.dnsenabled') == '1'
+ && !$attributes['fields']['email_only']
&& !$attributes['fields']['deactivated'];
}
@@ -169,7 +173,7 @@ class Domain
public static function hasLetsEncryptActivated(array $attributes): bool
{
- return ((bool)$attributes['fields']['letsencrypt'] && (!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0)));
+ return ((bool)$attributes['fields']['letsencrypt'] && (int)$attributes['fields']['email_only'] == 0);
}
/**
@@ -181,7 +185,7 @@ class Domain
&& DDomain::domainHasSslIpPort($attributes['fields']['id'])
&& (CurrentUser::isAdmin() || (!CurrentUser::isAdmin() && (int)$attributes['fields']['caneditdomain'] == 1))
&& (int)$attributes['fields']['letsencrypt'] == 0
- && (!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0))
+ && !(int)$attributes['fields']['email_only']
&& !$attributes['fields']['deactivated']
) {
return true;
diff --git a/lib/Froxlor/UI/Callbacks/Text.php b/lib/Froxlor/UI/Callbacks/Text.php
index 91df6fdf..c1a8cbef 100644
--- a/lib/Froxlor/UI/Callbacks/Text.php
+++ b/lib/Froxlor/UI/Callbacks/Text.php
@@ -52,6 +52,14 @@ class Text
];
}
+ public static function type2fa(array $attributes): array
+ {
+ return [
+ 'macro' => 'type2fa',
+ 'data' => (int)$attributes['data']
+ ];
+ }
+
public static function customerfullname(array $attributes): string
{
return User::getCorrectFullUserDetails($attributes['fields'], true);
diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php
index 47dde7bb..6e0ab1cc 100644
--- a/lib/formfields/customer/domains/formfield.domains_edit.php
+++ b/lib/formfields/customer/domains/formfield.domains_edit.php
@@ -47,13 +47,14 @@ return [
'values' => $domainips
],
'alias' => [
- 'visible' => $alias_check == '0',
+ 'visible' => $alias_check == '0' && (int)$result['email_only'] == 0,
'label' => lng('domains.aliasdomain'),
'type' => 'select',
'select_var' => $domains,
'selected' => $result['aliasdomain']
],
'path' => [
+ 'visible' => (int)$result['email_only'] == 0,
'label' => lng('panel.path'),
'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescriptionSubdomain').(Settings::Get('system.documentroot_use_default_value') == 1 ? lng('panel.pathDescriptionEx') : '') : null),
'type' => $pathSelect['type'],
@@ -63,13 +64,13 @@ return [
'note' => $pathSelect['note'] ?? '',
],
'url' => [
- 'visible' => Settings::Get('panel.pathedit') == 'Dropdown',
+ 'visible' => Settings::Get('panel.pathedit') == 'Dropdown' && (int)$result['email_only'] == 0,
'label' => lng('panel.urloverridespath'),
'type' => 'text',
'value' => $urlvalue
],
'redirectcode' => [
- 'visible' => Settings::Get('customredirect.enabled') == '1',
+ 'visible' => Settings::Get('customredirect.enabled') == '1' && (int)$result['email_only'] == 0,
'label' => lng('domains.redirectifpathisurl'),
'desc' => lng('domains.redirectifpathisurlinfo'),
'type' => 'select',
@@ -77,7 +78,7 @@ return [
'selected' => $def_code
],
'selectserveralias' => [
- 'visible' => ($result['parentdomainid'] == '0' && $userinfo['subdomains'] != '0') || $result['parentdomainid'] != '0',
+ 'visible' => (($result['parentdomainid'] == '0' && $userinfo['subdomains'] != '0') || $result['parentdomainid'] != '0') && (int)$result['email_only'] == 0,
'label' => lng('admin.selectserveralias'),
'desc' => lng('admin.selectserveralias_desc'),
'type' => 'select',
@@ -85,27 +86,28 @@ return [
'selected' => $serveraliasoptions_selected
],
'isemaildomain' => [
- 'visible' => ($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && $result['parentdomainid'] != '0',
+ 'visible' => (($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && $result['parentdomainid'] != '0') && (int)$result['email_only'] == 0,
'label' => 'Emaildomain',
'type' => 'checkbox',
'value' => '1',
'checked' => $result['isemaildomain']
],
'openbasedir_path' => [
- 'visible' => $result['openbasedir'] == '1',
+ 'visible' => $result['openbasedir'] == '1' && (int)$result['email_only'] == 0,
'label' => lng('domain.openbasedirpath'),
'type' => 'select',
'select_var' => $openbasedir,
'selected' => $result['openbasedir_path']
],
'phpsettingid' => [
- 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && count($phpconfigs) > 0 && $userinfo['phpenabled'] == '1' && $result['phpenabled'] == '1',
+ 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && count($phpconfigs) > 0 && $userinfo['phpenabled'] == '1' && $result['phpenabled'] == '1' && (int)$result['email_only'] == 0,
'label' => lng('admin.phpsettings.title'),
'type' => 'select',
'select_var' => $phpconfigs,
'selected' => $result['phpsettingid']
],
'speciallogfile' => [
+ 'visible' => (int)$result['email_only'] == 0,
'label' => lng('admin.speciallogfile.title'),
'desc' => lng('admin.speciallogfile.description'),
'type' => 'checkbox',
@@ -139,7 +141,7 @@ return [
'section_bssl' => [
'title' => lng('admin.webserversettings_ssl'),
'image' => 'icons/domain_edit.png',
- 'visible' => Settings::Get('system.use_ssl') == '1' && $ssl_ipsandports && Domain::domainHasSslIpPort($result['id']),
+ 'visible' => Settings::Get('system.use_ssl') == '1' && $ssl_ipsandports && Domain::domainHasSslIpPort($result['id']) && (int)$result['email_only'] == 0,
'fields' => [
'sslenabled' => [
'label' => lng('admin.domain_sslenabled'),
@@ -194,6 +196,7 @@ return [
]
]
]
- ]
+ ],
+ 'buttons' => ((int)$result['email_only'] == 1) ? [] : null
]
];
diff --git a/lib/tablelisting/admin/tablelisting.admins.php b/lib/tablelisting/admin/tablelisting.admins.php
index 637f3d4c..7af1eec3 100644
--- a/lib/tablelisting/admin/tablelisting.admins.php
+++ b/lib/tablelisting/admin/tablelisting.admins.php
@@ -110,6 +110,12 @@ return [
'class' => 'text-center',
'callback' => [Text::class, 'boolean'],
],
+ 'type_2fa' => [
+ 'label' => lng('2fa.type_2fa'),
+ 'field' => 'type_2fa',
+ 'class' => 'text-center',
+ 'callback' => [Text::class, 'type2fa'],
+ ],
],
'visible_columns' => Listing::getVisibleColumnsForListing('admin_list', [
'loginname',
diff --git a/lib/tablelisting/admin/tablelisting.customers.php b/lib/tablelisting/admin/tablelisting.customers.php
index de3e8fb5..6c1c7979 100644
--- a/lib/tablelisting/admin/tablelisting.customers.php
+++ b/lib/tablelisting/admin/tablelisting.customers.php
@@ -149,6 +149,12 @@ return [
'class' => 'text-center',
'callback' => [Text::class, 'boolean'],
],
+ 'c.type_2fa' => [
+ 'label' => lng('2fa.type_2fa'),
+ 'field' => 'type_2fa',
+ 'class' => 'text-center',
+ 'callback' => [Text::class, 'type2fa'],
+ ],
],
'visible_columns' => Listing::getVisibleColumnsForListing('customer_list', [
'c.name',
diff --git a/lib/tables.inc.php b/lib/tables.inc.php
index 26214333..0805f290 100644
--- a/lib/tables.inc.php
+++ b/lib/tables.inc.php
@@ -57,3 +57,4 @@ const TABLE_PANEL_PLANS = 'panel_plans';
const TABLE_API_KEYS = 'api_keys';
const TABLE_PANEL_USERCOLUMNS = 'panel_usercolumns';
const TABLE_PANEL_LOGINLINKS = 'panel_loginlinks';
+const TABLE_PANEL_2FA_TOKENS = 'panel_2fa_tokens';
diff --git a/lng/de.lng.php b/lng/de.lng.php
index 8a3eb506..167cf8ee 100644
--- a/lng/de.lng.php
+++ b/lng/de.lng.php
@@ -48,6 +48,7 @@ return [
'2fa_ga_desc' => 'Das Konto ist eingerichtet, um zeitbasierte Einmalpasswörter via Authenticator-App zu erhalten. Um die gewünschte Authenticator-App einzurichten, scanne bitte den untenstehenden QR-Code. Zum Deaktivieren, klicke auf "2FA deaktivieren"',
'2fa_not_activated' => 'Zwei-Faktor Authentifizierung ist nicht aktiviert',
'2fa_not_activated_for_user' => 'Zwei-Faktor Authentifizierung ist für den aktuellen Benutzer nicht aktiviert',
+ 'type_2fa' => '2FA Status',
],
'admin' => [
'overview' => 'Übersicht',
@@ -713,6 +714,7 @@ return [
'hsts' => 'HSTS aktiviert',
'aliasdomainid' => 'ID der Alias-Domain',
'nodomainsassignedbyadmin' => 'Diesem Account wurde noch keine (aktive) Domain zugewiesen. Bitte kontaktiere deinen Administrator, wenn du der Meinung bist, das ist nicht korrekt.',
+ 'email_only' => 'Nur E-Mail',
],
'emails' => [
'description' => 'Hier können Sie Ihre E-Mail-Adressen einrichten.
Ein Konto ist wie Ihr Briefkasten vor der Haustür. Wenn jemand eine E-Mail an Sie schreibt, wird diese in dieses Konto gelegt.
Die Zugangsdaten lauten wie folgt: (Die Angaben in kursiver Schrift sind durch die jeweiligen Einträge zu ersetzen)
Hostname: Domainname
Benutzername: Kontoname / E-Mail-Adresse
Passwort: das gewählte Passwort',
@@ -1031,6 +1033,7 @@ return [
'combination_not_found' => 'Kombination aus Benutzername und E-Mail Adresse stimmen nicht überein.',
'2fa' => 'Zwei-Faktor Authentifizierung (2FA)',
'2facode' => 'Bitte 2FA Code angeben',
+ '2faremember' => 'Browser vertrauen',
],
'mails' => [
'pop_success' => [
diff --git a/lng/en.lng.php b/lng/en.lng.php
index 112686c7..d51a4506 100644
--- a/lng/en.lng.php
+++ b/lng/en.lng.php
@@ -49,6 +49,7 @@ return [
'2fa_ga_desc' => 'Your account is set up to use time-based one-time passwords via authenticator-app. Please scan the QR code below with your desired authenticator app to generate the codes. To deactivate, click on "Deactivate 2FA"',
'2fa_not_activated' => 'Two-factor authentication is not enabled',
'2fa_not_activated_for_user' => 'Two-factor authentication is not enabled for the current user',
+ 'type_2fa' => '2FA status',
],
'admin' => [
'overview' => 'Overview',
@@ -784,6 +785,7 @@ return [
'hsts' => 'HSTS enabled',
'aliasdomainid' => 'ID of alias domain',
'nodomainsassignedbyadmin' => 'Your account has currently no (active) domains assigned to it. Please contact your administrator if you think this is wrong.',
+ 'email_only' => 'Email only',
],
'emails' => [
'description' => 'Here you can create and change your email addresses.
An account is like your letterbox in front of your house. If someone sends you an email, it will be dropped into the account.
To download your emails use the following settings in your mailprogram: (The data in italics has to be changed to the equivalents you typed in!)
Hostname: domainname
Username: account name / e-mail address
password: the password you\'ve chosen',
@@ -1103,6 +1105,7 @@ return [
'combination_not_found' => 'Combination of user and email address not found.',
'2fa' => 'Two-factor authentication (2FA)',
'2facode' => 'Please enter 2FA code',
+ '2faremember' => 'Trust browser',
],
'mails' => [
'pop_success' => [
diff --git a/logfiles_viewer.php b/logfiles_viewer.php
index 20d00d7c..e3a65872 100644
--- a/logfiles_viewer.php
+++ b/logfiles_viewer.php
@@ -61,6 +61,10 @@ if (function_exists('exec')) {
}
$domain = json_decode($json_result, true)['data'];
+ if ($domain['email_only']) {
+ Response::dynamicError("There are no webserver logfiles for email only domains.");
+ }
+
$speciallogfile = '';
if ($domain['speciallogfile'] == '1') {
if ($domain['parentdomainid'] == '0') {
diff --git a/ssl_editor.php b/ssl_editor.php
index b0d1c770..8fea36d9 100644
--- a/ssl_editor.php
+++ b/ssl_editor.php
@@ -50,6 +50,10 @@ if ($action == '' || $action == 'view') {
}
$result_domain = json_decode($json_result, true)['data'];
+ if ($result_domain['email_only']) {
+ Response::dynamicError("There are no ssl-certificates for email only domains.");
+ }
+
if (Request::post('send') == 'send') {
$do_insert = Request::post('do_insert', 0) == 1;
try {
diff --git a/templates/Froxlor/login/enter2fa.html.twig b/templates/Froxlor/login/enter2fa.html.twig
index b435bb0b..37cc3d66 100644
--- a/templates/Froxlor/login/enter2fa.html.twig
+++ b/templates/Froxlor/login/enter2fa.html.twig
@@ -22,6 +22,14 @@
+