1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-26 08:11:33 +00:00

Merge pull request #6475 from mailcow/staging

Automatic PR to nightly from 2025-04-09T01:26:20Z
This commit is contained in:
FreddleSpl0it
2025-05-13 09:52:49 +02:00
committed by GitHub
45 changed files with 687 additions and 202 deletions

View File

@@ -32,6 +32,13 @@ import time
import magic
import re
skip_olefy = os.getenv('SKIP_OLEFY', '')
if skip_olefy.lower() in ['yes', 'y']:
print("SKIP_OLEFY=y, skipping Olefy...")
time.sleep(365 * 24 * 60 * 60)
sys.exit(0)
# merge variables from /etc/olefy.conf and the defaults
olefy_listen_addr_string = os.getenv('OLEFY_BINDADDRESS', '127.0.0.1,::1')
olefy_listen_port = int(os.getenv('OLEFY_BINDPORT', '10050'))
@@ -113,7 +120,7 @@ def oletools( stream, tmp_file_name, lid ):
out = bytes(out.decode('utf-8', 'ignore').replace(' ', ' ').replace('\t', '').replace('\n', '').replace('XLMMacroDeobfuscator: pywin32 is not installed (only is required if you want to use MS Excel)', ''), encoding="utf-8")
failed = False
if out.__len__() < 30:
logger.error('{} olevba returned <30 chars - rc: {!r}, response: {!r}, error: {!r}'.format(lid,cmd_tmp.returncode,
logger.error('{} olevba returned <30 chars - rc: {!r}, response: {!r}, error: {!r}'.format(lid,cmd_tmp.returncode,
out.decode('utf-8', 'ignore'), err.decode('utf-8', 'ignore')))
out = b'[ { "error": "Unhandled error - too short olevba response" } ]'
failed = True

View File

@@ -5,7 +5,7 @@ LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG APCU_PECL_VERSION=5.1.24
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
ARG IMAGICK_PECL_VERSION=3.7.0
ARG IMAGICK_PECL_VERSION=3.8.0
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MAILPARSE_PECL_VERSION=3.1.8
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$

View File

@@ -81,6 +81,27 @@ EOF
redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF NO ONE
fi
if [[ "${SKIP_OLEFY}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
if [[ -f /etc/rspamd/local.d/external_services.conf ]]; then
rm /etc/rspamd/local.d/external_services.conf
fi
else
cat <<EOF > /etc/rspamd/local.d/external_services.conf
oletools {
# default olefy settings
servers = "olefy:10055";
# needs to be set explicitly for Rspamd < 1.9.5
scan_mime_parts = true;
# mime-part regex matching in content-type or filename
# block all macros
extended = true;
max_size = 3145728;
timeout = 20.0;
retransmits = 1;
}
EOF
fi
# Provide additional lua modules
ln -s /usr/lib/$(uname -m)-linux-gnu/liblua5.1-cjson.so.0.0.0 /usr/lib/rspamd/cjson.so

View File

@@ -994,6 +994,7 @@ PID=$!
echo "Spawned cert_checks with PID ${PID}"
BACKGROUND_TASKS+=(${PID})
if [[ "${SKIP_OLEFY}" =~ ^([nN][oO]|[nN])+$ ]]; then
(
while true; do
if ! olefy_checks; then
@@ -1005,6 +1006,7 @@ done
PID=$!
echo "Spawned olefy_checks with PID ${PID}"
BACKGROUND_TASKS+=(${PID})
fi
(
while true; do

View File

@@ -79,7 +79,9 @@ if ($isSOGoRequest) {
}
}
if ($result === false){
$result = apppass_login($post['username'], $post['password'], array($post['service'] => true), array(
// If it's a SOGo Request, don't check for protocol access
$service = ($isSOGoRequest) ? false : array($post['service'] => true);
$result = apppass_login($post['username'], $post['password'], $service, array(
'is_internal' => true,
'remote_addr' => $post['real_rip']
));

View File

@@ -29,13 +29,23 @@ function auth_password_verify(request, password)
insecure = true
}
if c ~= 200 then
-- Returning PASSDB_RESULT_PASSWORD_MISMATCH will reset the user's auth cache entry.
-- Returning PASSDB_RESULT_INTERNAL_FAILURE keeps the existing cache entry,
-- even if the TTL has expired. Useful to avoid cache eviction during backend issues.
if c ~= 200 and c ~= 401 then
dovecot.i_info("HTTP request failed with " .. c .. " for user " .. request.user)
return dovecot.auth.PASSDB_RESULT_INTERNAL_FAILURE, "Upstream error"
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Upstream error"
end
local api_response = json.decode(table.concat(res))
if api_response.success == true then
local response_str = table.concat(res)
local is_response_valid, response_json = pcall(json.decode, response_str)
if not is_response_valid then
dovecot.i_info("Invalid JSON received: " .. response_str)
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Invalid response format"
end
if response_json.success == true then
return dovecot.auth.PASSDB_RESULT_OK, ""
end

View File

@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Tue Apr 1 00:20:51 UTC 2025
# Whitelist generated by Postwhite v3.4 on Thu May 1 00:21:10 UTC 2025
# https://github.com/stevejenkins/postwhite/
# 2067 total rules
# 2058 total rules
2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit
2a01:111:f403:8000::/50 permit
@@ -18,6 +18,7 @@
2a02:a60:0:5::/64 permit
2c0f:fb50:4000::/36 permit
2.207.151.53 permit
2.207.217.30 permit
3.70.123.177 permit
3.93.157.0/24 permit
3.94.40.108 permit
@@ -26,10 +27,8 @@
8.20.114.31 permit
8.25.194.0/23 permit
8.25.196.0/23 permit
8.39.54.0/23 permit
8.39.54.250/31 permit
8.40.222.0/23 permit
8.40.222.250/31 permit
8.36.116.0/24 permit
8.39.144.0/24 permit
12.130.86.238 permit
13.107.246.59 permit
13.110.208.0/21 permit
@@ -103,6 +102,7 @@
27.123.206.80/28 permit
31.25.48.222 permit
31.47.251.17 permit
31.186.239.0/24 permit
34.2.64.0/22 permit
34.2.68.0/23 permit
34.2.70.0/23 permit
@@ -121,6 +121,10 @@
34.2.90.0/23 permit
34.2.92.0/23 permit
34.2.94.0/23 permit
34.70.158.162 permit
34.74.74.140 permit
34.83.159.189 permit
34.141.160.224 permit
34.195.217.107 permit
34.212.163.75 permit
34.215.104.144 permit
@@ -132,6 +136,7 @@
35.190.247.0/24 permit
35.191.0.0/16 permit
35.205.92.9 permit
35.228.216.85 permit
35.242.169.159 permit
37.188.97.188 permit
37.218.248.47 permit
@@ -233,6 +238,7 @@
52.95.49.88/29 permit
52.96.91.34 permit
52.96.111.82 permit
52.96.172.98 permit
52.96.214.50 permit
52.96.222.194 permit
52.96.222.226 permit
@@ -272,7 +278,6 @@
54.244.54.130 permit
54.244.242.0/24 permit
54.255.61.23 permit
56.124.6.228 permit
57.103.64.0/18 permit
62.13.128.0/24 permit
62.13.129.128/25 permit
@@ -309,9 +314,6 @@
64.207.219.13 permit
64.207.219.14 permit
64.207.219.15 permit
64.207.219.24 permit
64.207.219.25 permit
64.207.219.26 permit
64.207.219.71 permit
64.207.219.72 permit
64.207.219.73 permit
@@ -321,9 +323,6 @@
64.207.219.77 permit
64.207.219.78 permit
64.207.219.79 permit
64.207.219.88 permit
64.207.219.89 permit
64.207.219.90 permit
64.207.219.135 permit
64.207.219.136 permit
64.207.219.137 permit
@@ -359,7 +358,6 @@
65.110.161.77 permit
65.123.29.213 permit
65.123.29.220 permit
65.154.166.0/24 permit
65.212.180.36 permit
66.102.0.0/20 permit
66.119.150.192/26 permit
@@ -1323,9 +1321,6 @@
117.120.16.0/21 permit
119.42.242.52/31 permit
119.42.242.156 permit
121.244.91.48 permit
121.244.91.52 permit
122.15.156.182 permit
123.126.78.64/29 permit
124.108.96.24/31 permit
124.108.96.28/31 permit
@@ -1388,21 +1383,7 @@
134.170.141.64/26 permit
134.170.143.0/24 permit
134.170.174.0/24 permit
135.84.80.0/24 permit
135.84.81.0/24 permit
135.84.82.0/24 permit
135.84.83.0/24 permit
135.84.216.0/22 permit
136.143.160.0/24 permit
136.143.161.0/24 permit
136.143.162.0/24 permit
136.143.176.0/24 permit
136.143.177.0/24 permit
136.143.178.49 permit
136.143.182.0/23 permit
136.143.184.0/24 permit
136.143.188.0/24 permit
136.143.190.0/23 permit
136.147.128.0/20 permit
136.147.135.0/24 permit
136.147.176.0/20 permit
@@ -1417,7 +1398,6 @@
139.138.46.219 permit
139.138.57.55 permit
139.138.58.119 permit
139.167.79.86 permit
139.180.17.0/24 permit
140.238.148.191 permit
141.148.159.229 permit
@@ -1452,6 +1432,7 @@
146.20.215.0/24 permit
146.20.215.182 permit
146.88.28.0/24 permit
146.148.116.76 permit
147.154.32.0/25 permit
147.243.1.47 permit
147.243.1.48 permit
@@ -1533,11 +1514,10 @@
163.114.132.120 permit
163.114.134.16 permit
163.114.135.16 permit
163.116.128.0/17 permit
164.152.23.32 permit
164.152.25.241 permit
164.177.132.168/30 permit
165.173.128.0/24 permit
165.173.180.250/31 permit
165.173.182.250/31 permit
166.78.68.0/22 permit
166.78.68.221 permit
166.78.69.169 permit
@@ -1566,12 +1546,6 @@
168.245.12.252 permit
168.245.46.9 permit
168.245.127.231 permit
169.148.129.0/24 permit
169.148.131.0/24 permit
169.148.142.10 permit
169.148.144.0/25 permit
169.148.144.10 permit
169.148.146.0/23 permit
170.10.128.0/24 permit
170.10.129.0/24 permit
170.10.132.56/29 permit
@@ -1697,6 +1671,21 @@
193.123.56.63 permit
194.19.134.0/25 permit
194.64.234.129 permit
194.97.196.0/24 permit
194.97.196.3 permit
194.97.196.4 permit
194.97.196.11 permit
194.97.196.12 permit
194.97.204.0/24 permit
194.97.204.3 permit
194.97.204.4 permit
194.97.204.11 permit
194.97.204.12 permit
194.97.212.0/24 permit
194.97.212.3 permit
194.97.212.4 permit
194.97.212.11 permit
194.97.212.12 permit
194.106.220.0/23 permit
194.113.24.0/22 permit
194.154.193.192/27 permit
@@ -1733,15 +1722,7 @@
199.16.156.0/22 permit
199.33.145.1 permit
199.33.145.32 permit
199.34.22.36 permit
199.59.148.0/22 permit
199.67.80.2 permit
199.67.80.20 permit
199.67.82.2 permit
199.67.82.20 permit
199.67.84.0/24 permit
199.67.86.0/24 permit
199.67.88.0/24 permit
199.101.161.130 permit
199.101.162.0/25 permit
199.122.120.0/21 permit
@@ -1798,8 +1779,6 @@
204.92.114.187 permit
204.92.114.203 permit
204.92.114.204/31 permit
204.141.32.0/23 permit
204.141.42.0/23 permit
204.220.160.0/21 permit
204.220.168.0/21 permit
204.220.176.0/20 permit
@@ -2046,15 +2025,27 @@
2001:0868:0100:0600::/64 permit
2001:4860:4000::/36 permit
2001:748:100:40::2:0/112 permit
2001:748:400:1300::3 permit
2001:748:400:1300::4 permit
2001:748:400:1301::0/64 permit
2001:748:400:1301::3 permit
2001:748:400:1301::4 permit
2001:748:400:2300::3 permit
2001:748:400:2300::4 permit
2001:748:400:2301::0/64 permit
2001:748:400:2301::3 permit
2001:748:400:2301::4 permit
2001:748:400:3300::3 permit
2001:748:400:3300::4 permit
2001:748:400:3301::0/64 permit
2001:748:400:3301::3 permit
2001:748:400:3301::4 permit
2404:6800:4000::/36 permit
2603:1010:3:3::5b permit
2603:1020:201:10::10f permit
2603:1030:20e:3::23c permit
2603:1030:b:3::152 permit
2603:1030:c02:8::14 permit
2607:13c0:0001:0000:0000:0000:0000:7000/116 permit
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
2607:f8b0:4000::/36 permit
2620:109:c003:104::/64 permit
2620:109:c003:104::215 permit

View File

@@ -24,7 +24,6 @@
/.+\.guru$/i
/.+\.icu$/i
/.+\.id$/i
/.+\.info$/i
/.+\.in.net$/i
/.+\.ir$/i
/.+\.jetzt$/i

View File

@@ -8,7 +8,7 @@ VIRUS_FOUND {
}
# Bad policy from free mail providers
FREEMAIL_POLICY_FAILURE {
expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST& !WHITELISTED_FWD_HOST & -g+:policies";
expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST & !WHITELISTED_FWD_HOST & -g+:policies";
score = 16.0;
}
# Applies to freemail with undisclosed recipients

View File

@@ -1,12 +0,0 @@
oletools {
# default olefy settings
servers = "olefy:10055";
# needs to be set explicitly for Rspamd < 1.9.5
scan_mime_parts = true;
# mime-part regex matching in content-type or filename
# block all macros
extended = true;
max_size = 3145728;
timeout = 20.0;
retransmits = 1;
}

View File

@@ -16,6 +16,9 @@
SOGoFoldersSendEMailNotifications = YES;
SOGoForwardEnabled = YES;
// Added with SOGo 5.12 - Allows users to cleanup there maildirectories by deleting mails oder than X
SOGoEnableMailCleaning = YES;
// Fixes "MODIFICATION_FAILED" error (HTTP 412) in Clients when accepting invitations from external services
SOGoDisableOrganizerEventCheck = YES;
@@ -91,7 +94,7 @@
//SoDebugBaseURL = YES;
//ImapDebugEnabled = YES;
//SOGoEASDebugEnabled = YES;
SOGoEASSearchInBody = YES; // Experimental. Enabled since 2023-10
SOGoEASSearchInBody = YES;
//LDAPDebugEnabled = YES;
//PGDebugEnabled = YES;
//MySQL4DebugEnabled = YES;

View File

@@ -18,6 +18,7 @@ elseif (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] !=
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
$clamd_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_CLAMD"])) ? false : true;
$olefy_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_OLEFY"])) ? false : true;
if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CACHE')) {
@@ -33,6 +34,7 @@ $vmail_df = explode(',', (string)json_decode(docker('post', 'dovecot-mailcow', '
// containers
$containers_info = (array) docker('info');
if ($clamd_status === false) unset($containers_info['clamd-mailcow']);
if ($olefy_status === false) unset($containers_info['olefy-mailcow']);
ksort($containers_info);
$containers = array();
foreach ($containers_info as $container => $container_info) {
@@ -77,6 +79,7 @@ $template_data = [
'gal' => @$_SESSION['gal'],
'license_guid' => license('guid'),
'clamd_status' => $clamd_status,
'olefy_status' => $olefy_status,
'containers' => $containers,
'ip_check' => customize('get', 'ip_check'),
'lang_admin' => json_encode($lang['admin']),

View File

@@ -22,7 +22,8 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
$template = 'admin_index.twig';
$template_data = [
'login_delay' => @$_SESSION['ldelay']
'login_delay' => @$_SESSION['ldelay'],
'custom_login' => customize('get', 'custom_login'),
];
$js_minifier->add('/web/js/site/index.js');

View File

@@ -125,6 +125,7 @@ $template_data = [
'logo_specs' => customize('get', 'main_logo_specs'),
'logo_dark_specs' => customize('get', 'main_logo_dark_specs'),
'ip_check' => customize('get', 'ip_check'),
'custom_login' => customize('get', 'custom_login'),
'password_complexity' => password_complexity('get'),
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
'cors_settings' => $cors_settings,

View File

@@ -5847,6 +5847,7 @@ paths:
client_id: "mailcow_client"
client_secret: "*"
redirect_url: "https://mail.mailcow.tld"
redirect_url_extra: ["https://extramail.mailcow.tld"]
version: "26.1.3"
default_template: "Default"
mappers:
@@ -5900,6 +5901,9 @@ paths:
redirect_url:
description: The redirect URL that OIDC Provider will use after authentication. Required if `authsource` is keycloak or generic-oidc.
type: string
redirect_url_extra:
description: Additional redirect URLs that OIDC Provider can use after authentication if valid.
type: array
version:
description: Specifies the Keycloak version. Required if `authsource` is keycloak.
type: string
@@ -5990,6 +5994,7 @@ paths:
client_id: "mailcow_client"
client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
redirect_url: "https://mail.mailcow.tld"
redirect_url_extra: ["https://extramail.mailcow.tld"]
version: "26.1.3"
default_template: "Default"
mappers: ["small_mbox", "medium_mbox"]
@@ -6034,6 +6039,7 @@ paths:
client_id: "mailcow_client"
client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
redirect_url: "https://mail.mailcow.tld"
redirect_url_extra: ["https://extramail.mailcow.tld"]
client_scopes: "openid profile email mailcow_template"
default_template: "Default"
mappers: ["small_mbox", "medium_mbox"]

View File

@@ -85,7 +85,7 @@ if (count($records) == 0 || $records[0]['target'] != '') { ?>
<authentication>password-cleartext</authentication>
</outgoingServer>
<enable visiturl="https://<?=$mailcow_hostname; ?><?php if ($port != 443) echo ':'.$port; ?>/admin.php">
<enable visiturl="https://<?=$mailcow_hostname; ?><?php if ($port != 443) echo ':'.$port; ?>/admin">
<instruction>If you didn't change the password given to you by the administrator or if you didn't change it in a long time, please consider doing that now.</instruction>
<instruction lang="de">Sollten Sie das Ihnen durch den Administrator vergebene Passwort noch nicht geändert haben, empfehlen wir dies nun zu tun. Auch ein altes Passwort sollte aus Sicherheitsgründen geändert werden.</instruction>
</enable>

View File

@@ -22,6 +22,7 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
$template = 'domainadmin_index.twig';
$template_data = [
'login_delay' => @$_SESSION['ldelay'],
'custom_login' => customize('get', 'custom_login'),
];
$js_minifier->add('/web/js/site/index.js');

View File

@@ -242,6 +242,7 @@ function user_login($user, $pass, $extra = null){
return false;
}
$row['attributes'] = json_decode($row['attributes'], true);
switch ($row['authsource']) {
case 'keycloak':
// user authsource is keycloak, try using via rest flow
@@ -351,6 +352,11 @@ function user_login($user, $pass, $extra = null){
}
// verify password
if (verify_hash($row['password'], $pass) !== false) {
if (intval($row['attributes']['force_pw_update']) == 1) {
$_SESSION['pending_pw_update'] = true;
}
// check for tfa authenticators
$authenticators = get_tfa($user);
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
@@ -471,6 +477,9 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){
}
return false;
}
if (!$iam_provider) {
return false;
}
// get access_token for service account of mailcow client
$admin_token = identity_provider("get-keycloak-admin-token");
@@ -540,6 +549,17 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){
return 'user';
}
// check if login provisioning is enabled before creating user
if (!$iam_settings['login_provisioning']){
if (!$is_internal){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, "Auto-create users on login is deactivated"),
'msg' => 'login_failed'
);
}
return false;
}
// check if matching attribute exist
if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
if (!empty($iam_settings['default_template'])) {
@@ -653,10 +673,21 @@ function ldap_mbox_login($user, $pass, $extra = null){
return 'user';
}
// check if login provisioning is enabled before creating user
if (!$iam_settings['login_provisioning']){
if (!$is_internal){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, "Auto-create users on login is deactivated"),
'msg' => 'login_failed'
);
}
return false;
}
// check if matching attribute exist
if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
if (!empty($iam_settings['default_tempalte'])) {
$mbox_template = $iam_settings['default_tempalte'];
if (!empty($iam_settings['default_template'])) {
$mbox_template = $iam_settings['default_template'];
} else {
$_SESSION['return'][] = array(
'type' => 'danger',

View File

@@ -204,6 +204,35 @@ function customize($_action, $_item, $_data = null) {
'msg' => 'ip_check_opt_in_modified'
);
break;
case 'custom_login':
$hide_user_quicklink = ($_data['hide_user_quicklink'] == "1") ? 1 : 0;
$hide_domainadmin_quicklink = ($_data['hide_domainadmin_quicklink'] == "1") ? 1 : 0;
$hide_admin_quicklink = ($_data['hide_admin_quicklink'] == "1") ? 1 : 0;
$force_sso = ($_data['force_sso'] == "1") ? 1 : 0;
$custom_login = array(
"hide_user_quicklink" => $hide_user_quicklink,
"hide_domainadmin_quicklink" => $hide_domainadmin_quicklink,
"hide_admin_quicklink" => $hide_admin_quicklink,
"force_sso" => $force_sso,
);
try {
$redis->set('CUSTOM_LOGIN', json_encode($custom_login));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'custom_login_modified'
);
break;
}
break;
case 'delete':
@@ -357,6 +386,20 @@ function customize($_action, $_item, $_data = null) {
return false;
}
break;
case 'custom_login':
try {
$custom_login = ($custom_login = $redis->get('CUSTOM_LOGIN')) ? $custom_login : array();
return json_decode($custom_login, true);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
}
break;
}

View File

@@ -1001,6 +1001,7 @@ function edit_user_account($_data) {
':password_hashed' => $password_hashed,
':username' => $username
));
$_SESSION['pending_pw_update'] = false;
update_sogo_static_view();
}
@@ -2286,12 +2287,14 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row){
switch ($row["key"]) {
case "redirect_url_extra":
case "mappers":
case "templates":
$settings[$row["key"]] = json_decode($row["value"]);
break;
case "use_ssl":
case "use_tls":
case "login_provisioning":
case "ignore_ssl_errors":
$settings[$row["key"]] = boolval($row["value"]);
break;
@@ -2300,6 +2303,10 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
break;
}
}
// set login_provisioning if not exists
if (!array_key_exists('login_provisioning', $settings)) {
$settings['login_provisioning'] = 1;
}
// return default client_scopes for generic-oidc if none is set
if ($settings["authsource"] == "generic-oidc" && empty($settings["client_scopes"])){
$settings["client_scopes"] = "openid profile email mailcow_template";
@@ -2364,7 +2371,8 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
return false;
}
$_data['ignore_ssl_error'] = isset($_data['ignore_ssl_error']) ? boolval($_data['ignore_ssl_error']) : false;
$_data['ignore_ssl_error'] = isset($_data['ignore_ssl_error']) ? boolval($_data['ignore_ssl_error']) : false;
$_data['login_provisioning'] = isset($_data['login_provisioning']) ? boolval($_data['login_provisioning']) : false;
switch ($_data['authsource']) {
case "keycloak":
$_data['server_url'] = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
@@ -2373,14 +2381,14 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval', 'ignore_ssl_error');
$required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval', 'ignore_ssl_error', 'login_provisioning');
break;
case "generic-oidc":
$_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null;
$_data['token_url'] = (!empty($_data['token_url'])) ? $_data['token_url'] : null;
$_data['userinfo_url'] = (!empty($_data['userinfo_url'])) ? $_data['userinfo_url'] : null;
$_data['client_scopes'] = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email mailcow_template";
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'ignore_ssl_error');
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'ignore_ssl_error', 'login_provisioning');
break;
case "ldap":
$_data['host'] = (!empty($_data['host'])) ? str_replace(" ", "", $_data['host']) : "";
@@ -2394,7 +2402,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$_data['use_tls'] = isset($_data['use_tls']) && !$_data['use_ssl'] ? boolval($_data['use_tls']) : false;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error');
$required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error', 'login_provisioning');
break;
}
@@ -2418,6 +2426,18 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
}
$pdo->commit();
// add redirect_url_extra
if (isset($_data['redirect_url_extra'])){
$_data['redirect_url_extra'] = (!is_array($_data['redirect_url_extra'])) ? array($_data['redirect_url_extra']) : $_data['redirect_url_extra'];
$redirect_url_extra = array_filter($_data['redirect_url_extra']);
$redirect_url_extra = json_encode($redirect_url_extra);
$stmt = $pdo->prepare("INSERT INTO identity_provider (`key`, `value`) VALUES ('redirect_url_extra', :value) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);");
$stmt->bindParam(':value', $redirect_url_extra);
$stmt->execute();
}
// add default template
if (isset($_data['default_template'])) {
$_data['default_template'] = (empty($_data['default_template'])) ? "" : $_data['default_template'];
@@ -2752,6 +2772,16 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
return true;
}
// user doesn't exist, check if login provisioning is enabled
if (!$iam_settings['login_provisioning']){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, "Auto-create users on login is deactivated"),
'msg' => 'login_failed'
);
return false;
}
if (empty($iam_settings['mappers']) || empty($user_template) || $mapper_key === false){
if (!empty($iam_settings['default_template'])) {
$mbox_template = $iam_settings['default_template'];
@@ -2851,7 +2881,19 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
case "get-redirect":
if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc')
return false;
$authUrl = $iam_provider->getAuthorizationUrl();
$options = [];
if (isset($iam_settings['redirect_url_extra'])) {
// check if the current domain is used in an extra redirect URL
$targetDomain = strtolower($_SERVER['HTTP_HOST']);
foreach ($iam_settings['redirect_url_extra'] as $testUrl) {
$testUrlParsed = parse_url($testUrl);
if (isset($testUrlParsed['host']) && strtolower($testUrlParsed['host']) == $targetDomain) {
$options['redirect_uri'] = $testUrl;
break;
}
}
}
$authUrl = $iam_provider->getAuthorizationUrl($options);
$_SESSION['oauth2state'] = $iam_provider->getState();
return $authUrl;
break;

View File

@@ -169,7 +169,7 @@ function quarantine($_action, $_data = null) {
}
}
elseif ($release_format == 'raw') {
$detail_row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/', 'X-Pre-Release-Spam-Flag $1', $detail_row['msg']);
$detail_row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $detail_row['msg']);
$postfix_talk = array(
array('220', 'HELO quarantine' . chr(10)),
array('250', 'MAIL FROM: ' . $sender . chr(10)),
@@ -464,7 +464,7 @@ function quarantine($_action, $_data = null) {
}
}
elseif ($release_format == 'raw') {
$row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/', 'X-Pre-Release-Spam-Flag $1', $row['msg']);
$row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $row['msg']);
$postfix_talk = array(
array('220', 'HELO quarantine' . chr(10)),
array('250', 'MAIL FROM: ' . $sender . chr(10)),

View File

@@ -76,7 +76,10 @@ if (isset($_POST["verify_tfa_login"])) {
$user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']);
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) {
if (intval($user_details['attributes']['sogo_access']) == 1 &&
intval($user_details['attributes']['force_pw_update']) != 1 &&
getenv('SKIP_SOGO') != "y" &&
!$is_dual) {
header("Location: /SOGo/so/{$_SESSION['mailcow_cc_username']}");
die();
} else {
@@ -139,7 +142,10 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
$user_details = mailbox("get", "mailbox_details", $login_user);
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) {
if (intval($user_details['attributes']['sogo_access']) == 1 &&
intval($user_details['attributes']['force_pw_update']) != 1 &&
getenv('SKIP_SOGO') != "y" &&
!$is_dual) {
header("Location: /SOGo/so/{$login_user}");
die();
} else {

View File

@@ -11,7 +11,7 @@ if (isset($_SESSION['mailcow_cc_role']) && isset($_SESSION['oauth2_request'])) {
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
$user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']);
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) {
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual && getenv('SKIP_SOGO') != "y") {
header("Location: /SOGo/so/{$_SESSION['mailcow_cc_username']}");
} else {
header("Location: /user");
@@ -33,16 +33,18 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
$has_iam_sso = false;
if ($iam_provider){
$has_iam_sso = identity_provider("get-redirect") ? true : false;
$iam_redirect_url = identity_provider("get-redirect");
$has_iam_sso = $iam_redirect_url ? true : false;
}
$custom_login = customize('get', 'custom_login');
$template = 'user_index.twig';
$template_data = [
'oauth2_request' => @$_SESSION['oauth2_request'],
'is_mobileconfig' => str_contains($_SESSION['index_query_string'], 'mobileconfig'),
'login_delay' => @$_SESSION['ldelay'],
'has_iam_sso' => $has_iam_sso
'has_iam_sso' => $has_iam_sso,
'custom_login' => $custom_login,
];
$js_minifier->add('/web/js/site/index.js');

View File

@@ -51,7 +51,7 @@ jQuery(function($){
$('.submit_rspamd_regex').attr({"disabled": true});
});
$("#show_rspamd_global_filters").click(function() {
$.get("inc/ajax/show_rspamd_global_filters.php");
$.get("/inc/ajax/show_rspamd_global_filters.php");
$("#confirm_show_rspamd_global_filters").hide();
$("#rspamd_global_filters").removeClass("d-none");
});
@@ -558,7 +558,7 @@ jQuery(function($){
} else if (table == 'oauth2clientstable') {
$.each(data, function (i, item) {
item.action = '<div class="btn-group">' +
'<a href="/edit.php?oauth2client=' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="/edit/oauth2client/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-oauth2-client" data-api-url="delete/oauth2-client" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>';
item.scope = "profile";
@@ -573,7 +573,7 @@ jQuery(function($){
item.action = '<div class="btn-group">' +
'<a href="/edit/domainadmin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' +
'<a href="/domainadmin/?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' +
'</div>';
});
} else if (table == 'adminstable') {
@@ -655,7 +655,7 @@ jQuery(function($){
$(this).html('<i class="bi bi-arrow-repeat icon-spin"></i> ');
$.ajax({
type: 'GET',
url: 'inc/ajax/relay_check.php',
url: '/inc/ajax/relay_check.php',
dataType: 'text',
data: $('#test_relayhost_form').serialize(),
complete: function (data) {
@@ -789,6 +789,18 @@ jQuery(function($){
$('.iam_ldap_rolemap_del').click(async function(e){
deleteAttributeMappingRow(this, e);
});
$('.iam_redirect_add_keycloak').click(async function(e){
addRedirectUrlRow('#iam_keycloak_redirect_list', '.iam_keycloak_redirect_del', e);
});
$('.iam_redirect_add_generic').click(async function(e){
addRedirectUrlRow('#iam_generic_redirect_list', '.iam_generic_redirect_del', e);
});
$('.iam_keycloak_redirect_del').click(async function(e){
deleteRedirectUrlRow(this, e);
});
$('.iam_generic_redirect_del').click(async function(e){
deleteRedirectUrlRow(this, e);
});
// selecting identity provider
$('#iam_provider').on('change', function(){
// toggle password fields
@@ -833,4 +845,22 @@ jQuery(function($){
if ($(elem).parent().parent().parent().parent().children().length > 1)
$(elem).parent().parent().parent().remove();
}
function addRedirectUrlRow(list_id, del_class, e) {
e.preventDefault();
var parent = $(list_id)
$(parent).children().last().clone().appendTo(parent);
var newChild = $(parent).children().last();
$(newChild).find('input').val('');
$(del_class).off('click');
$(del_class).click(async function(e){
deleteRedirectUrlRow(this, e);
});
}
function deleteRedirectUrlRow(elem, e) {
e.preventDefault();
if ($(elem).parent().parent().parent().parent().children().length > 2)
$(elem).parent().parent().parent().remove();
}
});

View File

@@ -98,7 +98,7 @@ jQuery(function($){
var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
var service = '<div class="badge bg-secondary">' + item.service.toUpperCase() + '</div>';
var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-app-indicator"></i> ' + escapeHtml(item.app_password_name || "App") + '</a>' : '';
var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.he.net/ip/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";
var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.tools/prefix/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";
var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + item.location.toLowerCase() + '"></span>' : '';
var ip_data = real_rip + ip_location + app_password;

View File

@@ -1976,6 +1976,9 @@ if (isset($_GET['query'])) {
case "ip_check":
process_edit_return(customize('edit', 'ip_check', $attr));
break;
case "custom_login":
process_edit_return(customize('edit', 'custom_login', $attr));
break;
case "self":
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
process_edit_return(domain_admin('edit', $attr));

View File

@@ -0,0 +1 @@
{}

View File

@@ -134,6 +134,7 @@
"admin_domains": "Domain-Zuweisungen",
"admins": "Administratoren",
"admins_ldap": "LDAP-Administratoren",
"admin_quicklink": "Quicklink zur Admin-Loginseite ausblenden",
"advanced_settings": "Erweiterte Einstellungen",
"api_allow_from": "IP-Adressen oder Netzwerke (CIDR Notation) für Zugriff auf API",
"api_info": "Die API befindet sich noch in Entwicklung, die Dokumentation kann unter <a href=\"/api\">/api</a> abgerufen werden.",
@@ -155,6 +156,7 @@
"credentials_transport_warning": "<b>Warnung</b>: Das Hinzufügen einer neuen Regel bewirkt die Aktualisierung der Authentifizierungsdaten aller vorhandenen Einträge mit identischem Next Hop.",
"customer_id": "Kunde",
"customize": "UI-Anpassung",
"login_page": "Login-Seite",
"destination": "Ziel",
"dkim_add_key": "ARC/DKIM-Key hinzufügen",
"dkim_domains_selector": "Selector",
@@ -173,6 +175,7 @@
"domain": "Domain",
"domain_admin": "Administrator hinzufügen",
"domain_admins": "Domain-Administratoren",
"domainadmin_quicklink": "Quicklink zur Domainadmin-Loginseite ausblenden",
"domain_s": "Domain(s)",
"duplicate": "Duplizieren",
"duplicate_dkim": "DKIM duplizieren",
@@ -195,6 +198,8 @@
"f2b_retry_window": "Wiederholungen im Zeitraum von (s)",
"f2b_whitelist": "Whitelist für Netzwerke und Hosts",
"filter_table": "Tabelle filtern",
"force_sso_text": "Wenn ein externer OIDC-Provider konfiguriert ist, blendet diese Option die mailcow Loginform aus und zeigt nur den Single Sign-On-Button an.",
"force_sso": "mailcow Login deaktivieren und nur Single Sign-On anzeigen",
"forwarding_hosts": "Weiterleitungs-Hosts",
"forwarding_hosts_add_hint": "Sie können entweder IPv4-/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.",
"forwarding_hosts_hint": "Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.",
@@ -222,6 +227,7 @@
"iam_host": "Host",
"iam_host_info": "Gib einen oder mehrere LDAP-Hosts ein, getrennt durch Kommas.",
"iam_import_users": "Importiere Benutzer",
"iam_login_provisioning": "Benutzer beim Login erstellen",
"iam_mapping": "Attribut Mapping",
"iam_bindpass": "Bind Passwort",
"iam_periodic_full_sync": "Vollsynchronisation",
@@ -308,6 +314,7 @@
"quarantine_release_format_att": "Als Anhang",
"quarantine_release_format_raw": "Unverändertes Original",
"quarantine_retention_size": "Rückhaltungen pro Mailbox:<br><small>0 bedeutet <b>inaktiv</b>.</small>",
"quicklink_text": "Quicklinks zu anderen Login-Seiten unter der Loginform ein- oder ausblenden",
"quota_notification_html": "Benachrichtigungs-E-Mail Inhalt:<br><small>Leer lassen, um Standard-Template wiederherzustellen.</small>",
"quota_notification_sender": "Benachrichtigungs-E-Mail Absender",
"quota_notification_subject": "Benachrichtigungs-E-Mail Betreff",
@@ -387,6 +394,7 @@
"unchanged_if_empty": "Unverändert, wenn leer",
"upload": "Hochladen",
"username": "Benutzername",
"user_quicklink": "Quicklink zur Benutzer-Loginseite ausblenden",
"validate_license_now": "GUID erneut verifizieren",
"verify": "Verifizieren",
"yes": "&#10003;",
@@ -398,7 +406,8 @@
"allowed_methods": "Access-Control-Allow-Methods",
"allowed_origins": "Access-Control-Allow-Origin",
"logo_dark_label": "Invertiert für den Darkmode",
"logo_normal_label": "Normal"
"logo_normal_label": "Normal",
"user_link": "Nutzer-Link"
},
"danger": {
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
@@ -806,9 +815,13 @@
"forgot_password": "> Passwort vergessen?",
"invalid_pass_reset_token": "Der Rücksetz-Token für das Passwort ist ungültig oder abgelaufen.<br>Bitte fordern Sie einen neuen Link zur Passwortwiederherstellung an.",
"login": "Anmelden",
"login_user": "Benutzer Anmelden",
"login_dadmin": "Domain-Administrator Anmelden",
"login_admin": "Administrator Anmelden",
"login_linkstext": "Nicht der richtige Login?",
"login_usertext": "Als Benutzer anmelden",
"login_domainadmintext": "Als Domainadmin anmelden",
"login_admintext": "Als Admin anmelden",
"login_user": "Anmeldung als Benutzer",
"login_dadmin": "Anmeldung als Domain-Administrator",
"login_admin": "Anmeldung als Administrator",
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
"new_password": "Neues Passwort",
"new_password_confirm": "Neues Passwort bestätigen",
@@ -991,7 +1004,8 @@
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
"syncjob_EXIT_OVERQUOTA": "Ziel Mailbox ist über dem Limit",
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kann keine Verbindung zum Zielserver herstellen"
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kann keine Verbindung zum Zielserver herstellen",
"iam": "Identitätsanbieter"
},
"oauth2": {
"access_denied": "Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.",
@@ -1094,6 +1108,7 @@
"bcc_edited": "BCC-Map-Eintrag %s wurde geändert",
"bcc_saved": "BCC- Map-Eintrag wurde gespeichert",
"cors_headers_edited": "CORS Einstellungen wurden erfolgreich gespeichert",
"custom_login_modified": "Login Anpassung wurde erfolgreich gespeichert",
"db_init_complete": "Datenbankinitialisierung abgeschlossen",
"delete_filter": "Filter-ID %s wurde gelöscht",
"delete_filters": "Filter gelöscht: %s",

View File

@@ -134,6 +134,7 @@
"admin_domains": "Domain assignments",
"admins": "Administrators",
"admins_ldap": "LDAP Administrators",
"admin_quicklink": "Hide Quicklink to Admin Login Page",
"advanced_settings": "Advanced settings",
"allowed_methods": "Access-Control-Allow-Methods",
"allowed_origins": "Access-Control-Allow-Origin",
@@ -161,6 +162,7 @@
"credentials_transport_warning": "<b>Warning</b>: Adding a new transport map entry will update the credentials for all entries with a matching next hop column.",
"customer_id": "Customer ID",
"customize": "Customize",
"login_page": "Login Page",
"destination": "Destination",
"dkim_add_key": "Add ARC/DKIM key",
"dkim_domains_selector": "Selector",
@@ -179,6 +181,7 @@
"domain": "Domain",
"domain_admin": "Domain administrator",
"domain_admins": "Domain administrators",
"domainadmin_quicklink": "Hide Quicklink to Domainadmin Login Page",
"domain_s": "Domain/s",
"duplicate": "Duplicate",
"duplicate_dkim": "Duplicate DKIM record",
@@ -202,6 +205,8 @@
"f2b_whitelist": "Whitelisted networks/hosts",
"filter": "Filter",
"filter_table": "Filter table",
"force_sso_text": "If an external OIDC provider is configured, this option hides the default mailcow login froms and only shows the Singe Sign-On button",
"force_sso": "Disable mailcow Login and show only Singe Sign-On",
"forwarding_hosts": "Forwarding Hosts",
"forwarding_hosts_add_hint": "You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).",
"forwarding_hosts_hint": "Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected, but optionally it can be filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your mailcow server.",
@@ -229,6 +234,7 @@
"iam_host": "Host",
"iam_host_info": "Enter one or more LDAP hosts, separated by commas.",
"iam_import_users": "Import Users",
"iam_login_provisioning": "Auto-create users on login",
"iam_mapping": "Attribute Mapping",
"iam_bindpass": "Bind Password",
"iam_periodic_full_sync": "Periodic Full Sync",
@@ -317,6 +323,7 @@
"quarantine_release_format_att": "As attachment",
"quarantine_release_format_raw": "Unmodified original",
"quarantine_retention_size": "Retentions per mailbox:<br><small>0 indicates <b>inactive</b>.</small>",
"quicklink_text": "Show or hide quick links to other login pages under the login form",
"quota_notification_html": "Notification email template:<br><small>Leave empty to restore default template.</small>",
"quota_notification_sender": "Notification email sender",
"quota_notification_subject": "Notification email subject",
@@ -398,6 +405,7 @@
"upload": "Upload",
"username": "Username",
"user_link": "User-Link",
"user_quicklink": "Hide Quicklink to User Login Page",
"validate_license_now": "Validate GUID against license server",
"verify": "Verify",
"yes": "&#10003;"
@@ -809,6 +817,10 @@
"forgot_password": "> Forgot Password?",
"invalid_pass_reset_token": "The reset password token is invalid or has expired.<br>Please request a new password reset link.",
"login": "Login",
"login_linkstext": "Not the correct login?",
"login_usertext": "Log in as user",
"login_domainadmintext": "Log in as domain admin",
"login_admintext": "Log in as admin",
"login_user": "User Login",
"login_dadmin": "Domain-Administrator Login",
"login_admin": "Administrator Login",
@@ -1105,6 +1117,7 @@
"bcc_edited": "BCC map entry %s edited",
"bcc_saved": "BCC map entry saved",
"cors_headers_edited": "CORS settings have been saved",
"custom_login_modified": "Login customisation was saved successfully",
"db_init_complete": "Database initialization completed",
"delete_filter": "Deleted filters ID %s",
"delete_filters": "Deleted filters: %s",

View File

@@ -730,7 +730,10 @@
"new_password": "Nouveau mot de passe",
"new_password_confirm": "Confirmer le nouveau mot de passe",
"reset_password": "Réinitialiser le mot de passe",
"request_reset_password": "Demander le changement du mot de passe"
"request_reset_password": "Demander le changement du mot de passe",
"login_user": "Connexion Utilisateur",
"login_dadmin": "Connexion Administrateur de domaine",
"login_admin": "Connexion Administrateur"
},
"mailbox": {
"action": "Action",

View File

@@ -590,4 +590,4 @@
"app_name": "Alkalmazás neve",
"app_passwd_protocols": "Engedélyezett protokollok az alkalmazás jelszavához"
}
}
}

View File

@@ -16,7 +16,16 @@
"login_as": "Pieteikšanās kā pastkastes lietotājam",
"mailbox_relayhost": "Pasta kastītes relayhost maiņa",
"prohibited": "Aizliegts ar ACL",
"protocol_access": "Protokola piekļuves maiņa"
"protocol_access": "Protokola piekļuves maiņa",
"pw_reset": "Ļaut atiestatīt mailcow lietotāja paroli",
"ratelimit": "Piekļuves biežuma ierobežojums",
"quarantine": "Karantīnas darbības",
"quarantine_attachments": "Karantīnas pielikumi",
"quarantine_category": "Mainīt karantīnas paziņojumu kategoriju",
"quarantine_notification": "Mainīt karantīnas paziņojumus",
"smtp_ip_access": "Mainīt SMTP atļautos saimniekdatorus",
"sogo_access": "Atļaut SOGo piekļuves pārvaldību",
"sogo_profile_reset": "Atiestatīt SOGo profilu"
},
"add": {
"activate_filter_warn": "Visi pārējie filtri tiks deaktivizēti, kad aktīvs ir atzīmēts.",
@@ -24,8 +33,8 @@
"add": "Pievienot",
"add_domain_only": "Tikai pievienot domēnu",
"add_domain_restart": "Pievienot domēnu un restartēt SOGo",
"alias_address": "Aizstājaddrese/s",
"alias_address_info": "<small>Pilna epasta addrese/s vai @piemērs.com, lai notvertu visas domēna ziņas (komatu atdalītas). <b>tikai mailcow domēni</b>.</small>",
"alias_address": "Aizstājadrese/s",
"alias_address_info": "<small>Pilna epasta adrese/s vai @example.com, lai notvertu visus domēna ziņojumus (atdalītas ar komatu). <b>Tikai mailcow domēni</b>.</small>",
"alias_domain": "Aizstājdomēni",
"alias_domain_info": "<small>Tikai derīgi domēna vārdi (komatu atdalīti).</small>",
"automap": "Mēģiniet automatizēt mapes (\"Nosūtītie vienumi\", \"Nosūtītie\" => \"Nosūtītie\" etc.)",
@@ -63,15 +72,17 @@
"skipcrossduplicates": "Izlaist dublētus ziņojumus pa mapēm (pirmais nāk, pirmais kalpo)",
"syncjob": "Pievienot sinhronizācijas darbu",
"syncjob_hint": "Ņemiet vērā, ka parole ir jāuzglabā vienkāršā tekstā!",
"target_address": "Iet uz adresēm",
"target_address_info": "<small>Pilna epasta addrese/s (comma-separated).</small>",
"target_address": "Mērķa adreses",
"target_address_info": "<small>Pilna epasta adrese/s (atdalītas ar komatu).</small>",
"target_domain": "Mērķa domēns",
"username": "Lietotājvārds",
"validate": "Apstiprināt",
"validation_success": "Apstiprināts veiksmīgi",
"bcc_dest_format": "BCC galamērķim ir jābūt vienai derīgai e-pasta adresei.<br>Ja ir nepieciešams nosūtīt kopiju vairākām adresēm, jāizveido aizstājvārds un jāizmanto tas šeit.",
"domain_matches_hostname": "Domēns %s atbilst saimniekdatora nosaukumam",
"disable_login": "Neļaut pieteikšanos (ienākošais pasts joprojām tiks pieņemts)"
"disable_login": "Neļaut pieteikšanos (ienākošais pasts joprojām tiks pieņemts)",
"app_password": "Pievienot lietotnes paroli",
"app_passwd_protocols": "Atļautie lietotnes paroles protokoli"
},
"admin": {
"access": "Pieeja",
@@ -114,7 +125,7 @@
"f2b_whitelist": "Baltā saraksta tīkls/hosts",
"filter_table": "Filtru tabula",
"forwarding_hosts": "Hostu pārsūtīšana",
"forwarding_hosts_add_hint": "Var norādīt vai nu IPv4/IPv6 addreses, tīklu ar CIDR apzīmējumu, saimniekdatoru nosaukumus (kas tiks atrisināti IP adresēs) vai arī domēna vārdus (kas tiks atrisināti IP adresēs, vaicājot SPF ierakstus, vai, ja tādu nav, MX ierakstus).",
"forwarding_hosts_add_hint": "Var norādīt vai nu IPv4/IPv6 adreses, tīklu ar CIDR apzīmējumu, saimniekdatoru nosaukumus (kas tiks atrisināti IP adresēs) vai arī domēna vārdus (kas tiks atrisināti IP adresēs, vaicājot SPF ierakstus, vai, ja tādu nav, MX ierakstus).",
"forwarding_hosts_hint": "Ienākošie ziņojumi tiek bez nosacījumiem pieņemti no visiem šeit norādītajiem saimniekdatoriem. Tie tad netiek pārbaudīti pret DNSBL vai pakļauti ievietošanai pelēkajā sarakstā. No tiem saņemtās mēstules nekad netiek noraidītas, bet pēc izvēles tās var pārvietot mapē \"Nevēlams\". Visbiežāk to izmanto, lai norādītu pasta serverus, kuros ir uzstādīts nosacījums, kas pārsūta ienākošās e-pasta vēstules uz Tavu mailcow serveri.",
"help_text": "Pārrakstīt palīdzības tekstu zem pieteikšanās maskas (var izmantot HTML)",
"host": "Hosts",
@@ -142,7 +153,7 @@
"recipients": "Adresāts",
"refresh": "Atsvaidzināt",
"regen_api_key": "Reģenerēt API atslēgu",
"relay_from": "\"No:\" addrese",
"relay_from": "\"No:\" adrese",
"relay_run": "Palaist testu",
"relayhosts_hint": "Norādīt no sūtītāja atkarīgas piegādes, lai varētu tos atlasīt domēnu konfigurācijas uzvednē.<br>\n Piegādes pakalpojums vienmēr ir \"smtp\", tādējādi tiks mēģināts TLS, kad piedāvāts. Iekļautais TLS (SMTPS) netiek atbalstīts. Tiek ņemts vērā lietotāja atsevišķais izejošā TLS nosacījuma iestatījums.<br>\n Ietekmē atlasītos domēnus, tajā skaitā aizstājdomēnus.",
"remove": "Noņemt",
@@ -222,7 +233,8 @@
"targetd_not_found": "Mērķa domēns nav atrasts",
"username_invalid": "Lietotājvārds nevar tikt izmantots",
"validity_missing": "Lūdzu piešķiriet derīguma termiņu",
"domain_cannot_match_hostname": "Domēns nevar atbilst saimniekdatora nosaukumam"
"domain_cannot_match_hostname": "Domēns nevar atbilst saimniekdatora nosaukumam",
"app_passwd_id_invalid": "Lietotnes paroles Id %s ir nederīgs"
},
"diagnostics": {
"cname_from_a": "Vērtība, kas iegūta no A/AAAA ieraksta. Tas tiek atbalstīts tik ilgi, kamēr ieraksts norāda uz pareizo resursu.",
@@ -257,7 +269,7 @@
"hostname": "Saimniekdatora nosaukums",
"inactive": "Neaktīvs",
"kind": "Veids",
"mailbox": "Rediģēt pastkasti",
"mailbox": "Labot pastkasti",
"max_aliases": "Lielākais aizstājvārdu skaits",
"max_mailboxes": "Maks. iespējamās pastkastes",
"max_quota": "Maks. kvota uz pastkasti (MiB)",
@@ -283,8 +295,8 @@
"spam_policy": "Pievienot vai noņemt vienumus baltajā-/melnajā sarakstā",
"spam_score": "Iestatīt pielāgotu surogātpasta vērtējumu",
"subfolder2": "Sinhronizēt galamērķa apakšmapē<br><small>(tukšs = neizmantot apakšmapi)</small>",
"syncjob": "Rediģēt sinhronizācijas darbu",
"target_address": "Iet uz adresi/ēm <small>(komatu atdalītas)</small>",
"syncjob": "Labot sinhronizācijas darbu",
"target_address": "Mērķa adrese/s <small>(atdalītas ar komatu)</small>",
"target_domain": "Mērķa domēns",
"title": "Labot priekšmetu",
"unchanged_if_empty": "Ja neizmainīts atstājiet tukšu",
@@ -300,8 +312,11 @@
"sogo_visible": "Aizstājvārds ir redzams SOGo",
"sogo_visible_info": "Šī iespēja ietekmē tikai tos objektus, kurus var parādīt SOGo (koplietojamās vai nekoplietojamās aizstājadreses, kas norāda uz vismaz vienu vietējo pastkasti). Ja paslēpts, netiks parādīts SOGo kā atlasāms sūtītājs.",
"mbox_rl_info": "Šis pieprasījumu ierobežojums tiek piemērots SASL pieteikšanās vārdam, tas atbilst jebkurai \"from\" adresei, ko izmanto lietotājs, kurš ir pieteicies. Pastkastes pieprasījumu ierobežojums pārraksta domēna mēroga pieprasījumu ierobežojumu.",
"sogo_access": "Nodrošināt tiešu pieteikšanās piekļuvi SOGo",
"disable_login": "Neļaut pieteikšanos (ienākošais pasts joprojām tiks pieņemts)"
"sogo_access": "Tieša pārvirzīšana uz SOGo",
"disable_login": "Neļaut pieteikšanos (ienākošais pasts joprojām tiks pieņemts)",
"app_passwd_protocols": "Atļautie lietotnes paroles protokoli",
"allowed_protocols": "Atļautie protokoli tiešai lietotāja piekļuvei (neietekmē lietotnes paroles protokolus)",
"app_passwd": "Lietotnes parole"
},
"footer": {
"cancel": "Atcelt",
@@ -508,7 +523,9 @@
"verified_fido2_login": "Apliecināta FIDO2 pieteikšanās",
"verified_webauthn_login": "Apliecināta WebAuthn pieteikšanās",
"verified_totp_login": "Apliecināta TOTP pieteikšanās",
"verified_yotp_login": "Apliecināta Yubico OTP pieteikšanās"
"verified_yotp_login": "Apliecināta Yubico OTP pieteikšanās",
"app_passwd_removed": "Noņemta lietotnes parole ar Id %s",
"app_passwd_added": "Pievienota jauna lietotnes parole"
},
"tfa": {
"api_register": "%s izmanto Yubico Cloud API. Lūdzu iegūstiet API atslēgu priekš Jūsu atslēgas<a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">here</a>",
@@ -523,7 +540,7 @@
"scan_qr_code": "Lūdzu, skenējiet šo kodu ar savu autentifikācijas lietojumprogrammu vai ievadiet kodu manuāli.",
"select": "Lūdzu izvēlaties",
"set_tfa": "Uzstādīt difi faktoru autentifik;acijas metodi",
"tfa": "Divu faktoru autentifikācija",
"tfa": "Divpakāpju pieteikšanās",
"totp": "Uz laiku bāzēta vienreizēja parole (Google Autentifikātors utt.)",
"webauthn": "WebAuthn autentifikācija",
"waiting_usb_auth": "<i>Gaida USB ierīci...</i><br><br>Lūdzu, tagad nospiežiet pogu uz Jūsu WebAuthn USB ierīces.",
@@ -618,12 +635,18 @@
"apple_connection_profile_mailonly": "Šis savienojuma profils iekļauj IMAP un SMTP konfigurācijas parametrus Apple ierīcei.",
"pushover_info": "Pašpiegādes paziņojumu iestatījumi attieksies uz visu tīro (ne surogātpasta) pastu, kas piegādāts uz <b>%s</b>, ieskaitot aizstājvārdus (kopīgotus, nekopīgotus, ar birkām).",
"app_hint": "Lietotņu paroles ir aizstājējparoles, lai pieteiktos IMAP, SMTP, CalDAV, CardDAV un EAS. Lietotājvārds paliek nemainīgs. SOGo tīmekļa pasts nav pieejams ar lietotņu parolēm.",
"direct_protocol_access": "Šim pastkastes lietotājam ir <b> tieša, ārēja piekļuve</b> zemāk uzskaitītajiem protokoliem un lietotnēm. Šo iestatījumu pārrauga pārvaldītājs. Lietotņu paroles var izveidot, lai nodrošinātu piekļuvi atsevišķiem protokoliem un lietotnēm.<br>Poga \"Pieteikties tīmekļa pastā\" nodrošina vienotu pieteikšanos SOGo un vienmēr ir pieejama.",
"direct_protocol_access": "Šim pastkastes lietotājam ir <b> tieša, ārēja piekļuve</b> zemāk uzskaitītajiem protokoliem un lietotnēm. Šo iestatījumu pārrauga pārvaldītājs. Lietotņu paroles var izveidot, lai nodrošinātu piekļuvi atsevišķiem protokoliem un lietotnēm.<br>Poga \"Tīmekļa pasts\" nodrošina vienotu pieteikšanos SOGo un vienmēr ir pieejama.",
"last_ui_login": "Pēdējā pieteikšanās saskarnē",
"login_history": "Pieteikšanās vēsture",
"no_last_login": "Nav informācijas par pēdējām pieteikšanās saskarnē reizēm",
"open_webmail_sso": "Pieteikšanās tīmekļa pastā",
"last_mail_login": "Pēdējā pasta pieteikšanās"
"open_webmail_sso": "Tīmekļa pasts",
"last_mail_login": "Pēdējā pasta pieteikšanās",
"change_password_hint_app_passwords": "Kontā ir %d lietotņu paroles, kas netiks mainītas. Lai pārvaldītu tās, jādodas uz cilni \"Lietotņu paroles\".",
"with_app_password": "ar lietotnes paroli",
"apple_connection_profile_with_app_password": "Jauna lietotnes parole ir izveidota un pievienota profilam, lai ierīces iestatīšanas laikā nebūtu nepieciešams ievadīt paroli. Lūgums nekopīgot datni, jo tā nodrošina pilnu piekļuvi pastkastei.",
"tfa_info": "Divpakāpju autentificēšanās palīdz aizsargāt kontu.Ja tā ir iespējota, var būt nepieciešamas lietotņu paroles, lai pieteiktos lietotnēs vai pakalpojumos, kas nenodrošina divpakāpju autentificēšanos (piem., e-pasta klienti).",
"app_passwds": "Lietotņu paroles",
"create_app_passwd": "Izveidot lietotnes paroli"
},
"datatables": {
"paginate": {
@@ -657,4 +680,4 @@
"fido2": {
"fido2_auth": "Pieteikties ar FIDO2"
}
}
}

View File

@@ -1309,4 +1309,4 @@
"q_reject": "Reddedildi",
"week": "Hafta"
}
}
}

View File

@@ -1307,4 +1307,4 @@
},
"collapse_all": "Згорнути все"
}
}
}

View File

@@ -108,7 +108,8 @@
"timeout2": "本地主机连接超时时间",
"username": "用户名",
"validate": "验证",
"validation_success": "验证成功"
"validation_success": "验证成功",
"dry": "模拟同步Dry run"
},
"admin": {
"access": "权限管理",
@@ -358,7 +359,43 @@
"ip_check_disabled": "IP 检查已禁用。你可透过以下路径启用<br> <strong>系统 > 配置 > 选项 > 页面自定义</strong>",
"queue_unban": "解除封禁",
"allowed_methods": "访问控制允许方式",
"allowed_origins": "访问控制允许原"
"allowed_origins": "访问控制允许原",
"iam": "身份识别提供者",
"iam_attribute_field": "Attribute 域",
"iam_authorize_url": "Authorization endpoint",
"iam_auth_flow": "认证流程",
"iam_basedn": "Base DN",
"iam_client_id": "客户端 ID",
"iam_client_secret": "客户端凭据",
"iam_client_scopes": "客户端 Scopes",
"iam_default_template": "默认模板",
"iam_default_template_description": "如果未为用户分配模板,则在创建邮箱时将使用默认模板,但在更新邮箱时不会使用默认模板。",
"iam_description": "配置外部认证提供者<br>如果已设置好属性映射,用户在首次登录时将会自动创建其 Mailbox。",
"iam_host": "Host",
"iam_host_info": "请输入一个或多个 LDAP 主机,使用英文逗号分隔。",
"iam_import_users": "导入用户",
"iam_mapping": "属性映射",
"iam_bindpass": "密码绑定Bind Password",
"iam_periodic_full_sync": "周期性全量同步",
"iam_port": "端口",
"iam_realm": "Realm",
"iam_redirect_url": "重定向 Url",
"iam_rest_flow": "Mailpassword 流程",
"iam_server_url": "服务器 Url",
"iam_sso": "单点登录SSO",
"iam_sync_interval": "同步/导入周期min",
"iam_test_connection": "测试连接",
"iam_token_url": "Token endpoint",
"iam_userinfo_url": "User info endpoint",
"iam_username_field": "Username 域",
"iam_binddn": "Bind DN",
"iam_use_ssl": "使用 SSL",
"iam_use_tls": "使用 TLS",
"iam_version": "版本",
"ignore_ssl_error": "忽略 SSL 错误",
"iam_auth_flow_info": "除了在单点登录SSO中使用的 Authorization Code 流程(在 Keycloak 中是标准流程之外mailcow 还支持使用 Credentials 的身份认证流程。Mailpassword 流程尝试通过 Keycloak 的 Admin REST API 验证用户凭据mailcow 会从 Keycloak 中的 <code>mailcow_password</code> 属性中获取哈希后的密码。",
"filter": "过滤",
"iam_extra_permission": "要使以下设置生效Keycloak 中的 mailcow 客户端需要一个 <code>服务账户Service account</code> 以及 <code>查看用户view-users</code> 的权限。"
},
"danger": {
"access_denied": "访问被拒绝或者表单数据无效",
@@ -495,7 +532,11 @@
"webauthn_authenticator_failed": "找不到所选的 authenticator",
"webauthn_publickey_failed": "没有为选定的身份验证器保存公钥",
"webauthn_username_failed": "所选的 authenticator 属于另一个账户",
"demo_mode_enabled": "演示模式已开启"
"demo_mode_enabled": "演示模式已开启",
"generic_server_error": "服务器错误。请联系您的管理员。",
"authsource_in_use": "由于当前有一个或多个用户正在使用该身份提供者IDP因此无法更改或删除。",
"iam_test_connection": "连接失败",
"required_data_missing": "缺少需要的 %s 数据"
},
"debug": {
"chart_this_server": "图表 (此服务器)",
@@ -744,7 +785,10 @@
"new_password_confirm": "确认新密码",
"reset_password": "重置密码",
"request_reset_password": "请求重置密码",
"invalid_pass_reset_token": "密码重置 token 无效或已过期。<br> 请重新获取新的密码重置链接。"
"invalid_pass_reset_token": "密码重置 token 无效或已过期。<br> 请重新获取新的密码重置链接。",
"login_user": "用户登录",
"login_dadmin": "域管理员登录",
"login_admin": "管理员登录"
},
"mailbox": {
"action": "操作",
@@ -919,7 +963,8 @@
"max_quota": "每个信箱的最大容量配额",
"relay_unknown": "转发未知信箱",
"templates": "模板",
"template": "模板"
"template": "模板",
"iam": "身份提供者IDP"
},
"oauth2": {
"access_denied": "请作为邮箱所有者登录以使用 OAuth2 授权。",
@@ -950,7 +995,7 @@
"neutral_danger": "无危险等级",
"notified": "已发送通知",
"qhandler_success": "已成功向系统发送请求,现在你可以关闭这个窗口了。",
"qid": "Rspamd QID",
"qid": "Rspamd 队列IDQID",
"qinfo": "隔离系统会把已被拒绝接收的邮件以及作为拷贝发送到垃圾箱的邮件保存到数据库中 (发件人<em>不</em>会知道)。\r\n <br>\"学习为垃圾并删除\" 会根据贝叶斯定理将消息作为垃圾学习并计算其模糊特征以拒绝未来收到相似消息。\r\n <br>请注意,这取决于你的系统资源,学习多个消息可能会花费较长时间。<br>黑名单中项目会被隔离系统排除。",
"qitem": "隔离项目",
"quarantine": "隔离",
@@ -1097,7 +1142,8 @@
"recovery_email_sent": "重置邮件已发送至 %s",
"template_added": "新增了模板 %s",
"template_modified": "模板 %s 的修改已保存",
"template_removed": "模板 ID %s 已删除"
"template_removed": "模板 ID %s 已删除",
"iam_test_connection": "连接成功"
},
"tfa": {
"api_register": "%s 使用了 Yubico Cloud API请<a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">在此</a>为你的密钥获取 API 密钥",
@@ -1292,7 +1338,11 @@
"password_reset_info": "如果不提供密码重置邮箱,此功能将无法使用。",
"pushover_sound": "声音",
"value": "值",
"attribute": "属性"
"attribute": "属性",
"protocols": "协议",
"authentication": "认证",
"tfa_info": "两步验证有助于保护您的账户安全。启用后,对于不支持两步验证的应用程序或服务(例如邮件客户端),需要使用应用专用密码进行登录。",
"overview": "概览"
},
"warning": {
"cannot_delete_self": "不能删除已登录的用户",
@@ -1332,4 +1382,4 @@
"loadingRecords": "加载中...",
"zeroRecords": "未找到符合条件的记录"
}
}
}

View File

@@ -94,7 +94,8 @@ elseif (isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HT
!empty($email) &&
filter_var($email, FILTER_VALIDATE_EMAIL) &&
is_array($_SESSION[$session_var_user_allowed]) &&
in_array($email, $_SESSION[$session_var_user_allowed])
in_array($email, $_SESSION[$session_var_user_allowed]) &&
!$_SESSION['pending_pw_update']
) {
$username = $email;
$password = file_get_contents("/etc/sogo-sso/sogo-sso.pass");

View File

@@ -51,7 +51,41 @@
</div></p>
</form>
</div>
<legend>{{ lang.admin.app_links }}</legend><hr />
<legend style="padding-top:20px" unselectable="on">{{ lang.admin.login_page }}</legend><hr />
<div>
<form class="form" data-id="custom_login" role="form" method="post">
<p class="text-muted">{{ lang.admin.quicklink_text }}</p>
<div class="ms-2 mb-1">
<input class="form-check-input" type="checkbox" value="1" name="hide_user_quicklink" id="hide_user_quicklink" {% if custom_login.hide_user_quicklink == 1 %}checked{% endif %}>
<label class="form-check-label" for="hide_user_quicklink">
{{ lang.admin.user_quicklink|raw }}
</label>
</div>
<div class="ms-2 mb-1">
<input class="form-check-input" type="checkbox" value="1" name="hide_domainadmin_quicklink" id="hide_domainadmin_quicklink" {% if custom_login.hide_domainadmin_quicklink == 1 %}checked{% endif %}>
<label class="form-check-label" for="hide_domainadmin_quicklink">
{{ lang.admin.domainadmin_quicklink|raw }}
</label>
</div>
<div class="ms-2 mb-4">
<input class="form-check-input" type="checkbox" value="1" name="hide_admin_quicklink" id="hide_admin_quicklink" {% if custom_login.hide_admin_quicklink == 1 %}checked{% endif %}>
<label class="form-check-label" for="hide_admin_quicklink">
{{ lang.admin.admin_quicklink|raw }}
</label>
</div>
<p class="text-muted">{{ lang.admin.force_sso_text|raw }}</p>
<div class="ms-2 mb-4">
<input class="form-check-input" type="checkbox" value="1" name="force_sso" id="force_sso" {% if custom_login.force_sso == 1 %}checked{% endif %}>
<label class="form-check-label" for="force_sso">
{{ lang.admin.force_sso|raw }}
</label>
</div>
<p><div class="btn-group">
<button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-success" data-action="edit_selected" data-item="admin" data-id="custom_login" data-reload="no" data-api-url='edit/custom_login' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
</div></p>
</form>
</div>
<legend style="padding-top:20px">{{ lang.admin.app_links }}</legend><hr />
<p class="text-muted">{{ lang.admin.merged_vars_hint|raw }}</p>
<form class="form-inline" data-id="app_links" role="form" method="post">
<table class="table table-condensed" style="white-space: nowrap;" id="app_link_table">

View File

@@ -110,7 +110,7 @@
<p>
<span class="badge fs-7 bg-info d-block d-sm-inline-block">
<i class="bi bi-funnel-fill"></i>
<a href="https://bgp.he.net/ip/{{ active_ban.ip }}" target="_blank">
<a href="https://bgp.tools/prefix/{{ active_ban.ip }}" target="_blank">
{{ active_ban.network }}
</a>
({{ active_ban.banned_until }})
@@ -130,7 +130,7 @@
<p>
<span class="badge fs-7 bg-danger d-block d-sm-inline-block">
<i class="bi bi-funnel-fill"></i>
<a href="https://bgp.he.net/ip/{{ perm_ban.ip }}" target="_blank">
<a href="https://bgp.tools/prefix/{{ perm_ban.ip }}" target="_blank">
{{ perm_ban.network }}
</a>
</span>

View File

@@ -64,10 +64,42 @@
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_keycloak_redirecturl">{{ lang.admin.iam_redirect_url }}:</label>
<label class="control-label">{{ lang.admin.iam_redirect_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_keycloak_redirecturl" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
<div class="row px-2 align-items-center">
<span class="col-10 p-0 pe-2">
<input type="text" class="form-control" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
</span>
<div class="col-2 p-0 d-flex">
<button class="btn btn-sm d-block d-sm-inline btn-secondary ms-auto iam_redirect_add_keycloak"><i class="bi bi-plus-lg"></i></button>
</div>
</div>
</div>
</div>
<div class="row mb-2" id="iam_keycloak_redirect_list">
<input type="hidden" name="redirect_url_extra" value="">
{% for key, url in iam_settings.redirect_url_extra %}
<div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
<div class="row px-2">
<div class="col-10 p-0 pe-2">
<input type="text" class="form-control me-2" name="redirect_url_extra" value="{{ iam_settings.redirect_url_extra[key] }}">
</div>
<div class="col-2 p-0 d-flex">
<button class="iam_keycloak_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
</div>
</div>
</div>
{% endfor %}
<div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
<div class="row px-2">
<div class="col-10 p-0 pe-2">
<input type="text" class="form-control me-2" name="redirect_url_extra" value="">
</div>
<div class="col-2 p-0 d-flex">
<button class="iam_keycloak_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
</div>
</div>
</div>
</div>
<div class="row mb-4">
@@ -187,6 +219,16 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_login_provisioning }}</label>
</div>
<div class="col-12 col-md-9">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="login_provisioning" value="1" {% if iam_settings.login_provisioning == 1 %}checked{% endif %}>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_periodic_full_sync }}</label>
@@ -274,10 +316,42 @@
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_redirect_url">{{ lang.admin.iam_redirect_url }}:</label>
<label class="control-label">{{ lang.admin.iam_redirect_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_redirect_url" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
<div class="row px-2 align-items-center">
<span class="col-10 p-0 pe-2">
<input type="text" class="form-control" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
</span>
<div class="col-2 p-0 d-flex">
<button class="btn btn-sm d-block d-sm-inline btn-secondary ms-auto iam_redirect_add_generic"><i class="bi bi-plus-lg"></i></button>
</div>
</div>
</div>
</div>
<div class="row mb-2" id="iam_generic_redirect_list">
<input type="hidden" name="redirect_url_extra" value="">
{% for key, url in iam_settings.redirect_url_extra %}
<div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
<div class="row px-2">
<div class="col-10 p-0 pe-2">
<input type="text" class="form-control me-2" name="redirect_url_extra" value="{{ iam_settings.redirect_url_extra[key] }}">
</div>
<div class="col-2 p-0 d-flex">
<button class="iam_generic_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
</div>
</div>
</div>
{% endfor %}
<div class="offset-md-3 col-12 col-md-9 col-lg-4 mb-2">
<div class="row px-2">
<div class="col-10 p-0 pe-2">
<input type="text" class="form-control me-2" name="redirect_url_extra" value="">
</div>
<div class="col-2 p-0 d-flex">
<button class="iam_generic_redirect_del btn btn-sm d-block d-sm-inline btn-secondary ms-auto"><i class="bi bi-x-lg"></i></button>
</div>
</div>
</div>
</div>
<div class="row mb-4">
@@ -366,7 +440,7 @@
</div>
</div>
</div>
<div class="row mb-4">
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.ignore_ssl_error }}</label>
</div>
@@ -376,6 +450,16 @@
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_login_provisioning }}</label>
</div>
<div class="col-12 col-md-9">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="login_provisioning" value="1" {% if iam_settings.login_provisioning == 1 %}checked{% endif %}>
</div>
</div>
</div>
<div class="row mt-4 mb-2">
<div class="offset-md-3 col-12 col-md-9 d-flex flex-wrap">
<div class="btn-group mb-2">
@@ -582,6 +666,16 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_login_provisioning }}</label>
</div>
<div class="col-12 col-md-9">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="login_provisioning" value="1" {% if iam_settings.login_provisioning == 1 %}checked{% endif %}>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_periodic_full_sync }}</label>

View File

@@ -5,13 +5,28 @@
{% block content %}
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card-header d-flex align-items-center">
<div class="card-header d-flex align-items-center text-break">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login_admin }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
</div>
<div class="ms-4 d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="text-secondary btn p-0 border-0 bg-transparent ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="card-body">
<div class="text-center mailcow-logo mb-4">
@@ -37,23 +52,10 @@
</div>
</div>
<div class="d-flex justify-content-between mt-4" style="position: relative">
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button> <div class="d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-secondary ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
<button type="submit" class="btn btn-xs-lg btn-success w-100 mt-2 mx-auto" style="max-width: 400px;" value="Login">{{ lang.login.login }}</button>
</div>
</form>
<div class="hr-title mt-5"><strong>{{ lang.login.other_logins }}</strong></div>
<div class="hr-title"><strong>{{ lang.login.other_logins }}</strong></div>
<div class="d-flex flex-column align-items-center">
<a class="btn btn-xs-lg btn-secondary w-100" style="max-width: 400px;" href="#" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</a>
</div>
@@ -86,6 +88,15 @@
{% endif %}
</div>
</div>
{% if custom_login.hide_user_quicklink != 1 or custom_login.hide_domainadmin_quicklink != 1 %}
<p class="text-center mt-3 text-muted" style="font-size: 0.9rem;">
{{ lang.login.login_linkstext }}<br>
{% if custom_login.hide_user_quicklink != 1 %}<a href="/">{{ lang.login.login_usertext }}</a>{% endif %}
{% if custom_login.hide_user_quicklink != 1 and custom_login.hide_domainadmin_quicklink != 1 %}|{% endif %}
{% if custom_login.hide_domainadmin_quicklink != 1 %}<a href="/domainadmin">{{ lang.login.login_domainadmintext }}</a>{% endif %}
</p>
{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -5,13 +5,28 @@
{% block content %}
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card-header d-flex align-items-center">
<div class="card-header d-flex align-items-center text-break">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login_dadmin }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
</div>
<div class="ms-4 d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="text-secondary btn p-0 border-0 bg-transparent ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="card-body">
<div class="text-center mailcow-logo mb-4">
@@ -37,23 +52,10 @@
</div>
</div>
<div class="d-flex justify-content-between mt-4" style="position: relative">
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button> <div class="d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-secondary ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
<button type="submit" class="btn btn-xs-lg btn-success w-100 mt-2 mx-auto" style="max-width: 400px;" value="Login">{{ lang.login.login }}</button>
</div>
</form>
<div class="hr-title mt-5"><strong>{{ lang.login.other_logins }}</strong></div>
<div class="hr-title"><strong>{{ lang.login.other_logins }}</strong></div>
<div class="d-flex flex-column align-items-center">
<a class="btn btn-xs-lg btn-secondary w-100" style="max-width: 400px;" href="#" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</a>
</div>
@@ -86,6 +88,15 @@
{% endif %}
</div>
</div>
{% if custom_login.hide_user_quicklink != 1 or custom_login.hide_admin_quicklink != 1 %}
<p class="text-center mt-3 text-muted" style="font-size: 0.9rem;">
{{ lang.login.login_linkstext }}<br>
{% if custom_login.hide_user_quicklink != 1 %}<a href="/">{{ lang.login.login_usertext }}</a>{% endif %}
{% if custom_login.hide_user_quicklink != 1 and custom_login.hide_admin_quicklink != 1 %}|{% endif %}
{% if custom_login.hide_admin_quicklink != 1 %}<a href="/admin">{{ lang.login.login_admintext }}</a>{% endif %}
</p>
{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -5,13 +5,30 @@
{% block content %}
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card-header d-flex align-items-center">
<div class="card-header d-flex align-items-center text-break">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login_user }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
</div>
{% if not oauth2_request %}
<div class="ms-4 d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="text-secondary btn p-0 border-0 bg-transparent ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
<div class="card-body">
<div class="text-center mailcow-logo mb-4">
@@ -25,6 +42,7 @@
{% if is_mobileconfig %}
<div class="my-4 alert alert-info ">{{ lang.login.mobileconfig_info }}</div>
{% endif %}
{% if custom_login.force_sso != 1 %}
<form method="post" autofill="off">
<div class="d-flex mt-3">
<label class="visually-hidden" for="login_user">{{ lang.login.username }}</label>
@@ -40,35 +58,22 @@
<input name="pass_user" type="password" id="pass_user" class="form-control" placeholder="{{ lang.login.password }}" required="" autocomplete="current-password">
</div>
</div>
<div class="mt-2 text-muted" style="font-size: 0.9rem;">
<a href="/reset-password">{{ lang.login.forgot_password }}</a>
</div>
<div class="d-flex justify-content-between mt-4" style="position: relative">
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button>
{% if not oauth2_request %}
<div class="d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-secondary ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<button type="submit" class="btn btn-xs-lg btn-success w-100 mt-2 mx-auto" style="max-width: 400px;" value="Login">{{ lang.login.login }}</button>
</div>
</form>
<div class="mt-3">
<a href="/reset-password">{{ lang.login.forgot_password }}</a>
</div>
<div class="hr-title mt-5"><strong>{{ lang.login.other_logins }}</strong></div>
<div class="hr-title"><strong>{{ lang.login.other_logins }}</strong></div>
{% endif %}
<div class="d-flex flex-column align-items-center">
{% if has_iam_sso %}
<a class="btn btn-xs-lg btn-secondary w-100 mt-2" style="max-width: 400px;" href="/?iam_sso=1"><i class="bi bi-cloud-arrow-up-fill"></i> {{ lang.admin.iam_sso }}</a>
{% endif %}
{% if custom_login.force_sso != 1 %}
<a class="btn btn-xs-lg btn-secondary w-100 mt-2" style="max-width: 400px;" href="#" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</a>
{% endif %}
</div>
{% if login_delay %}
<p><div class="my-4 alert alert-info">{{ lang.login.delayed|format(login_delay) }}</b></div></p>
@@ -96,9 +101,20 @@
{% endfor %}
{% endfor %}
</div>
<div>
</div>
{% endif %}
</div>
</div>
{% if custom_login.hide_admin_quicklink != 1 or custom_login.hide_domainadmin_quicklink != 1 %}
<p class="text-center mt-3 text-muted" style="font-size: 0.9rem;">
{{ lang.login.login_linkstext }}<br>
{% if custom_login.hide_admin_quicklink != 1 %}<a href="/admin">{{ lang.login.login_admintext }}</a>{% endif %}
{% if custom_login.hide_admin_quicklink != 1 and custom_login.hide_domainadmin_quicklink != 1 %}|{% endif %}
{% if custom_login.hide_domainadmin_quicklink != 1 %}<a href="/domainadmin">{{ lang.login.login_domainadmintext }}</a>{% endif %}
</p>
{% endif %}
</div>
</div>
{% if not oauth2_request and ui_texts.help_text %}

View File

@@ -84,7 +84,7 @@ services:
- clamd
rspamd-mailcow:
image: ghcr.io/mailcow/rspamd:2.1
image: ghcr.io/mailcow/rspamd:2.2
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@@ -176,6 +176,7 @@ services:
- COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized}
- SKIP_FTS=${SKIP_FTS:-y}
- SKIP_CLAMD=${SKIP_CLAMD:-n}
- SKIP_OLEFY=${SKIP_OLEFY:-n}
- SKIP_SOGO=${SKIP_SOGO:-n}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- MASTER=${MASTER:-y}
@@ -497,7 +498,7 @@ services:
- /lib/modules:/lib/modules:ro
watchdog-mailcow:
image: ghcr.io/mailcow/watchdog:2.07
image: ghcr.io/mailcow/watchdog:2.08
dns:
- ${IPV4_NETWORK:-172.22.1}.254
tmpfs:
@@ -538,6 +539,7 @@ services:
- IP_BY_DOCKER_API=${IP_BY_DOCKER_API:-0}
- CHECK_UNBOUND=${CHECK_UNBOUND:-1}
- SKIP_CLAMD=${SKIP_CLAMD:-n}
- SKIP_OLEFY=${SKIP_OLEFY:-n}
- SKIP_LETS_ENCRYPT=${SKIP_LETS_ENCRYPT:-n}
- SKIP_SOGO=${SKIP_SOGO:-n}
- HTTPS_PORT=${HTTPS_PORT:-443}
@@ -589,7 +591,7 @@ services:
- dockerapi
olefy-mailcow:
image: ghcr.io/mailcow/olefy:1.14
image: ghcr.io/mailcow/olefy:1.15
restart: always
environment:
- TZ=${TZ}
@@ -601,6 +603,7 @@ services:
- OLEFY_LOGLVL=20
- OLEFY_MINLENGTH=500
- OLEFY_DEL_TMP=1
- SKIP_OLEFY=${SKIP_OLEFY:-n}
networks:
mailcow-network:
aliases:

View File

@@ -382,6 +382,10 @@ SKIP_UNBOUND_HEALTHCHECK=n
SKIP_CLAMD=${SKIP_CLAMD}
# Skip Olefy (olefy-mailcow) anti-virus for Office documents (Rspamd will auto-detect a missing Olefy container) - y/n
SKIP_OLEFY=n
# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n
SKIP_SOGO=n

View File

@@ -323,6 +323,7 @@ adapt_new_options() {
"WATCHDOG_EXTERNAL_CHECKS"
"WATCHDOG_SUBJECT"
"SKIP_CLAMD"
"SKIP_OLEFY"
"SKIP_IP_CHECK"
"ADDITIONAL_SAN"
"DOVEADM_PORT"
@@ -967,6 +968,7 @@ CONFIG_ARRAY=(
"WATCHDOG_EXTERNAL_CHECKS"
"WATCHDOG_SUBJECT"
"SKIP_CLAMD"
"SKIP_OLEFY"
"SKIP_IP_CHECK"
"ADDITIONAL_SAN"
"AUTODISCOVER_SAN"
@@ -1278,6 +1280,18 @@ for option in "${CONFIG_ARRAY[@]}"; do
echo '# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost' >> mailcow.conf
echo 'DISABLE_NETFILTER_ISOLATION_RULE=n' >> mailcow.conf
fi
elif [[ "${option}" == "SKIP_CLAMD" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n' >> mailcow.conf
echo 'SKIP_CLAMD=n' >> mailcow.conf
fi
elif [[ "${option}" == "SKIP_OLEFY" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Skip Olefy (olefy-mailcow) anti-virus for Office documents (Rspamd will auto-detect a missing Olefy container) - y/n' >> mailcow.conf
echo 'SKIP_OLEFY=n' >> mailcow.conf
fi
elif [[ "${option}" == "REDISPASS" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"