diff --git a/index.php b/index.php
index 205b051726..dd3f267376 100644
--- a/index.php
+++ b/index.php
@@ -72,6 +72,7 @@
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 @@
$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 @@
'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 @@
// 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 299560de0f..5ffdb4a639 100644
--- a/install/froxlor.sql.php
+++ b/install/froxlor.sql.php
@@ -730,8 +730,8 @@
('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 @@
`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 9e96a98a1d..46645b5e1b 100644
--- a/install/updates/froxlor/update_2.2.inc.php
+++ b/install/updates/froxlor/update_2.2.inc.php
@@ -122,3 +122,26 @@
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 60e81f5974..c52d6dc56a 100644
--- a/lib/Froxlor/Api/Commands/Emails.php
+++ b/lib/Froxlor/Api/Commands/Emails.php
@@ -270,15 +270,6 @@ public function update()
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 @@ public function update()
$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 6c796234c0..1c4870c202 100644
--- a/lib/Froxlor/Api/Commands/SubDomains.php
+++ b/lib/Froxlor/Api/Commands/SubDomains.php
@@ -983,9 +983,11 @@ public function listing()
'`d`.`letsencrypt`',
'`d`.`registration_date`',
'`d`.`termination_date`',
- '`d`.`deactivated`'
+ '`d`.`deactivated`',
+ '`d`.`email_only`',
];
}
+
$query_fields = [];
// prepare select statement
@@ -996,7 +998,6 @@ public function listing()
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 @@ public function listingCount()
$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 61926ecc10..7045de8ec4 100644
--- a/lib/Froxlor/Cli/MasterCron.php
+++ b/lib/Froxlor/Cli/MasterCron.php
@@ -171,8 +171,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
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 a8ca8771c0..b49a7e22a7 100644
--- a/lib/Froxlor/Dns/Dns.php
+++ b/lib/Froxlor/Dns/Dns.php
@@ -54,7 +54,7 @@ public static function getAllowedDomainEntry(int $domain_id, string $area = 'cus
$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 83bc550119..0a5011e37e 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 b8fb3f68dd..f635ae2584 100644
--- a/lib/Froxlor/UI/Callbacks/Domain.php
+++ b/lib/Froxlor/UI/Callbacks/Domain.php
@@ -74,6 +74,9 @@ public static function domainTarget(array $attributes)
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 @@ public static function canEdit(array $attributes): bool
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 @@ public static function canEditDNS(array $attributes): bool
&& $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 @@ public static function adminCanEditDNS(array $attributes): bool
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 @@ public static function canEditSSL(array $attributes): bool
&& 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 91df6fdf7d..c1a8cbef2c 100644
--- a/lib/Froxlor/UI/Callbacks/Text.php
+++ b/lib/Froxlor/UI/Callbacks/Text.php
@@ -52,6 +52,14 @@ public static function yesno(array $attributes): array
];
}
+ 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 47dde7bb6a..6e0ab1cc12 100644
--- a/lib/formfields/customer/domains/formfield.domains_edit.php
+++ b/lib/formfields/customer/domains/formfield.domains_edit.php
@@ -47,13 +47,14 @@
'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 @@
'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 @@
'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 @@
'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 @@
'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 @@
]
]
]
- ]
+ ],
+ 'buttons' => ((int)$result['email_only'] == 1) ? [] : null
]
];
diff --git a/lib/tablelisting/admin/tablelisting.admins.php b/lib/tablelisting/admin/tablelisting.admins.php
index 637f3d4c4b..7af1eec389 100644
--- a/lib/tablelisting/admin/tablelisting.admins.php
+++ b/lib/tablelisting/admin/tablelisting.admins.php
@@ -110,6 +110,12 @@
'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 de3e8fb503..6c1c7979e2 100644
--- a/lib/tablelisting/admin/tablelisting.customers.php
+++ b/lib/tablelisting/admin/tablelisting.customers.php
@@ -149,6 +149,12 @@
'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 26214333d5..0805f29064 100644
--- a/lib/tables.inc.php
+++ b/lib/tables.inc.php
@@ -57,3 +57,4 @@
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 8a3eb506ba..167cf8eebe 100644
--- a/lng/de.lng.php
+++ b/lng/de.lng.php
@@ -48,6 +48,7 @@
'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 @@
'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 @@
'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 112686c709..d51a45064c 100644
--- a/lng/en.lng.php
+++ b/lng/en.lng.php
@@ -49,6 +49,7 @@
'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 @@
'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 @@
'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 20d00d7c4c..e3a65872fa 100644
--- a/logfiles_viewer.php
+++ b/logfiles_viewer.php
@@ -61,6 +61,10 @@
}
$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 b0d1c77051..8fea36d945 100644
--- a/ssl_editor.php
+++ b/ssl_editor.php
@@ -50,6 +50,10 @@
}
$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 b435bb0b55..37cc3d6687 100644
--- a/templates/Froxlor/login/enter2fa.html.twig
+++ b/templates/Froxlor/login/enter2fa.html.twig
@@ -22,6 +22,14 @@
+