mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-07-28 19:14:59 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e245ac04d9 | ||
|
|
fea38c8e1b | ||
|
|
cc9af65852 | ||
|
|
92cc8bec90 | ||
|
|
145745329e | ||
|
|
8e72d22c56 | ||
|
|
ddd76d99cd |
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
||||
uses: actions/stale@v11.0.0
|
||||
uses: actions/stale@v10.4.0
|
||||
with:
|
||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||
days-before-stale: 60
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM nginx:1.30.4-alpine
|
||||
FROM nginx:1.30.3-alpine
|
||||
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ENV PIP_BREAK_SYSTEM_PACKAGES=1
|
||||
|
||||
@@ -42,14 +42,17 @@ http {
|
||||
https https;
|
||||
}
|
||||
|
||||
map $server_port $sogo_auth_internal {
|
||||
65510 "1";
|
||||
default "";
|
||||
}
|
||||
|
||||
{% if HTTP_REDIRECT %}
|
||||
# HTTP to HTTPS redirect
|
||||
server {
|
||||
root /web;
|
||||
listen {{ HTTP_PORT }} default_server;
|
||||
{% if ENABLE_IPV6 %}
|
||||
listen [::]:{{ HTTP_PORT }} default_server;
|
||||
{%endif%}
|
||||
|
||||
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* mta-sts.* {{ ADDITIONAL_SERVER_NAMES | join(' ') }};
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ location ~ \.php$ {
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
# trusted internal-auth marker; empty for external clients (see nginx.conf map)
|
||||
fastcgi_param SOGO_AUTH_INTERNAL $sogo_auth_internal;
|
||||
fastcgi_read_timeout 3600;
|
||||
fastcgi_send_timeout 3600;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ $raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
|
||||
$raw_size = (int)$_FILES['message']['size'];
|
||||
|
||||
$qid = $meta['qid'] ?? 'unknown';
|
||||
$subject = $meta['subject'] ?? '';
|
||||
$subject = iconv_mime_decode($meta['subject'] ?? '');
|
||||
$score = $meta['score'] ?? 0;
|
||||
$rcpts = $meta['rcpt'] ?? array();
|
||||
$user = $meta['user'] ?? 'unknown';
|
||||
|
||||
@@ -50,7 +50,7 @@ $qid = $meta['qid'] ?? 'unknown';
|
||||
$rcpts = $meta['rcpt'] ?? array();
|
||||
$sender = $meta['from'] ?? '';
|
||||
$ip = $meta['ip'] ?? 'unknown';
|
||||
$subject = $meta['subject'] ?? '';
|
||||
$subject = iconv_mime_decode($meta['subject'] ?? '');
|
||||
$messageid= $meta['message_id'] ?? '';
|
||||
$priority = 0;
|
||||
|
||||
|
||||
@@ -185,9 +185,6 @@ paths:
|
||||
example:
|
||||
username: info@domain.tld
|
||||
domain: domain.tld
|
||||
description: my time limited alias
|
||||
validity: 8760
|
||||
permanent: false
|
||||
properties:
|
||||
username:
|
||||
description: 'the mailbox an alias should be created for'
|
||||
@@ -195,15 +192,6 @@ paths:
|
||||
domain:
|
||||
description: "the domain"
|
||||
type: string
|
||||
description:
|
||||
description: "a description for the alias, defaults to an empty string"
|
||||
type: string
|
||||
validity:
|
||||
description: "how many hours the alias stays valid, 1 to 87600, defaults to 8760 (one year)"
|
||||
type: integer
|
||||
permanent:
|
||||
description: "keep the alias after it expired, defaults to false"
|
||||
type: boolean
|
||||
type: object
|
||||
summary: Create time limited alias
|
||||
/api/v1/add/app-passwd:
|
||||
@@ -4920,7 +4908,6 @@ paths:
|
||||
quota: 3221225472
|
||||
quota_used: 0
|
||||
rl: false
|
||||
sender_acl: ["otherbox@doman3.tld", "@aliasdomain.tld"]
|
||||
spam_aliases: 0
|
||||
username: info@doman3.tld
|
||||
tags: ["tag1", "tag2"]
|
||||
@@ -5847,7 +5834,6 @@ paths:
|
||||
quota: 3221225472
|
||||
quota_used: 0
|
||||
rl: false
|
||||
sender_acl: ["otherbox@domain3.tld", "@aliasdomain.tld"]
|
||||
spam_aliases: 0
|
||||
username: info@domain3.tld
|
||||
tags: ["tag1", "tag2"]
|
||||
@@ -5870,7 +5856,7 @@ paths:
|
||||
response:
|
||||
value:
|
||||
- type: "success"
|
||||
log: ["cors", "edit", {"allowed_origins": ["*", "https://mail.mailcow.tld"], "allowed_methods": ["POST", "GET", "DELETE", "PUT"]}]
|
||||
log: ["cors", "edit", {"allowed_origins": ["*", "mail.mailcow.tld"], "allowed_methods": ["POST", "GET", "DELETE", "PUT"]}]
|
||||
msg: "cors_headers_edited"
|
||||
description: OK
|
||||
headers: { }
|
||||
@@ -5887,7 +5873,7 @@ paths:
|
||||
schema:
|
||||
example:
|
||||
attr:
|
||||
allowed_origins: ["*", "https://mail.mailcow.tld"]
|
||||
allowed_origins: ["*", "mail.mailcow.tld"]
|
||||
allowed_methods: ["POST", "GET", "DELETE", "PUT"]
|
||||
properties:
|
||||
attr:
|
||||
|
||||
@@ -429,14 +429,25 @@ function domain_admin_sso($_action, $_data) {
|
||||
|
||||
switch ($_action) {
|
||||
case 'check':
|
||||
$token = $_data;
|
||||
$token = preg_replace('/[^a-zA-Z0-9-]/', '', $_data);
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `t1`.`username` FROM `da_sso` AS `t1` JOIN `admin` AS `t2` ON `t1`.`username` = `t2`.`username` WHERE `t1`.`token` = :token AND `t1`.`created` > DATE_SUB(NOW(), INTERVAL '30' SECOND) AND `t2`.`active` = 1 AND `t2`.`superadmin` = 0;");
|
||||
$stmt->execute(array(
|
||||
':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token)
|
||||
':token' => $token
|
||||
));
|
||||
$return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return empty($return['username']) ? false : $return['username'];
|
||||
if (empty($return['username'])) {
|
||||
return false;
|
||||
}
|
||||
// single-use: consume the token; if a concurrent request already used it, deny
|
||||
$del = $pdo->prepare("DELETE FROM `da_sso` WHERE `token` = :token");
|
||||
$del->execute(array(
|
||||
':token' => $token
|
||||
));
|
||||
if ($del->rowCount() < 1) {
|
||||
return false;
|
||||
}
|
||||
return $return['username'];
|
||||
case 'issue':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
|
||||
@@ -53,28 +53,6 @@ function valid_network($network) {
|
||||
function valid_hostname($hostname) {
|
||||
return filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
|
||||
}
|
||||
// Validates a browser-style Origin: scheme://host[:port], no path/query/fragment/credentials
|
||||
function valid_origin($origin) {
|
||||
$parts = parse_url($origin);
|
||||
if ($parts === false || !isset($parts['scheme']) || !isset($parts['host'])) {
|
||||
return false;
|
||||
}
|
||||
if (!in_array(strtolower($parts['scheme']), array('http', 'https'), true)) {
|
||||
return false;
|
||||
}
|
||||
if (isset($parts['user']) || isset($parts['pass']) || isset($parts['path']) || isset($parts['query']) || isset($parts['fragment'])) {
|
||||
return false;
|
||||
}
|
||||
$host = $parts['host'];
|
||||
$host_without_brackets = trim($host, '[]');
|
||||
if (!valid_hostname($host_without_brackets) && !filter_var($host_without_brackets, FILTER_VALIDATE_IP)) {
|
||||
return false;
|
||||
}
|
||||
// Reject anything that doesn't round-trip to the exact same origin (e.g. stray trailing slash)
|
||||
$port = isset($parts['port']) ? ':' . $parts['port'] : '';
|
||||
$rebuilt = strtolower($parts['scheme']) . '://' . strtolower($host) . $port;
|
||||
return strtolower($origin) === $rebuilt;
|
||||
}
|
||||
// Thanks to https://stackoverflow.com/a/49373789
|
||||
// Validates exact ip matches and ip-in-cidr, ipv4 and ipv6
|
||||
function ip_acl($ip, $networks) {
|
||||
@@ -2305,13 +2283,10 @@ function cors($action, $data = null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$allowed_origins = isset($data['allowed_origins']) ? $data['allowed_origins'] : array(getBaseURL());
|
||||
$allowed_origins = isset($data['allowed_origins']) ? $data['allowed_origins'] : array($_SERVER['SERVER_NAME']);
|
||||
$allowed_origins = !is_array($allowed_origins) ? array_filter(array_map('trim', explode("\n", $allowed_origins))) : $allowed_origins;
|
||||
foreach ($allowed_origins as &$origin) {
|
||||
if ($origin === '*') {
|
||||
continue;
|
||||
}
|
||||
if (!valid_origin($origin)) {
|
||||
foreach ($allowed_origins as $origin) {
|
||||
if (!filter_var($origin, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) && $origin != '*') {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $action, $data),
|
||||
@@ -2319,10 +2294,7 @@ function cors($action, $data = null) {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// browsers always send a lowercase scheme/host in the Origin header, so normalize to match
|
||||
$origin = strtolower($origin);
|
||||
}
|
||||
unset($origin);
|
||||
|
||||
$allowed_methods = isset($data['allowed_methods']) ? $data['allowed_methods'] : array('GET', 'POST', 'PUT', 'DELETE');
|
||||
$allowed_methods = !is_array($allowed_methods) ? array_map('trim', preg_split( "/( |,|;|\n)/", $allowed_methods)) : $allowed_methods;
|
||||
|
||||
@@ -41,15 +41,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
else {
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
}
|
||||
if (isset($_data["validity"])) {
|
||||
if (!filter_var($_data["validity"], FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 87600)))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'validity_missing'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (isset($_data["validity"]) && !filter_var($_data["validity"], FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 87600)))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'validity_missing'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Default to 1 yr
|
||||
@@ -62,8 +60,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$permanent = 0;
|
||||
}
|
||||
$domain = $_data['domain'];
|
||||
// spamalias.description is NOT NULL, an absent description must not become a null insert
|
||||
$description = $_data['description'] ?? '';
|
||||
$description = $_data['description'];
|
||||
$valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain'];
|
||||
$valid_alias_domains = user_get_alias_details($username)['alias_domains'];
|
||||
if (!empty($valid_alias_domains)) {
|
||||
@@ -5323,14 +5320,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$mailboxdata['rl_scope'] = 'domain';
|
||||
}
|
||||
$mailboxdata['is_relayed'] = $row['backupmx'];
|
||||
// Internal send-as ACL for this mailbox, mirrors the sender_acl field accepted by edit/mailbox
|
||||
$mailboxdata['sender_acl'] = array();
|
||||
$stmt = $pdo->prepare("SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `external` = '0'");
|
||||
$stmt->execute(array(':username' => $_data));
|
||||
$sender_acl_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($sender_acl_rows as $sender_acl_row) {
|
||||
$mailboxdata['sender_acl'][] = $sender_acl_row['send_as'];
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `tag_name`
|
||||
FROM `tags_mailbox` WHERE `username`= :username");
|
||||
@@ -5658,8 +5647,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $username . "/%' OR `c_uid` = :username");
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($username, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
@@ -6055,8 +6047,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . str_replace('%', '\%', $username) . "/%' OR `c_uid` = :username");
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($username, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
@@ -6212,8 +6207,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $name
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $name . "/%' OR `c_uid` = :username");
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($name, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $name
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
|
||||
@@ -22,7 +22,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||
WHERE `qhash` = :hash
|
||||
WHERE `qhash` = :hash
|
||||
AND user_acl.quarantine = 1
|
||||
AND rcpt IN (SELECT username FROM mailbox)');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
@@ -65,7 +65,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||
WHERE `qhash` = :hash
|
||||
WHERE `qhash` = :hash
|
||||
AND `user_acl`.`quarantine` = 1
|
||||
AND `username` IN (SELECT `username` FROM `mailbox`)');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
@@ -170,6 +170,8 @@ function quarantine($_action, $_data = null) {
|
||||
}
|
||||
elseif ($release_format == 'raw') {
|
||||
$detail_row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $detail_row['msg']);
|
||||
// dot-stuffing per RFC5321 4.5.2: escape leading dots
|
||||
$detail_row['msg'] = preg_replace('~^\.~m', '..', $detail_row['msg']);
|
||||
$postfix_talk = array(
|
||||
array('220', 'HELO quarantine' . chr(10)),
|
||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||
@@ -180,7 +182,7 @@ function quarantine($_action, $_data = null) {
|
||||
array('221', '')
|
||||
);
|
||||
// Thanks to https://stackoverflow.com/questions/6632399/given-an-email-as-raw-text-how-can-i-send-it-using-php
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
if (!$smtp_connection) {
|
||||
logger(array('return' => array(
|
||||
array(
|
||||
@@ -192,7 +194,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
for ($i=0; $i < count($postfix_talk); $i++) {
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
if (substr($smtp_resource, 0, 3) !== $postfix_talk[$i][0]) {
|
||||
$ret = substr($smtp_resource, 0, 3);
|
||||
$ret = (empty($ret)) ? '-' : $ret;
|
||||
@@ -465,17 +467,19 @@ function quarantine($_action, $_data = null) {
|
||||
}
|
||||
elseif ($release_format == 'raw') {
|
||||
$row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $row['msg']);
|
||||
// dot-stuffing per RFC5321 4.5.2: escape leading dots
|
||||
$row['msg'] = preg_replace('~^\.~m', '..', $row['msg']);
|
||||
$postfix_talk = array(
|
||||
array('220', 'HELO quarantine' . chr(10)),
|
||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||
array('250', 'RCPT TO: ' . $row['rcpt'] . chr(10)),
|
||||
array('250', 'DATA' . chr(10)),
|
||||
array('354', str_replace("\n.", '', $row['msg']) . chr(10) . '.' . chr(10)),
|
||||
array('354', $row['msg'] . chr(10) . '.' . chr(10)),
|
||||
array('250', 'QUIT' . chr(10)),
|
||||
array('221', '')
|
||||
);
|
||||
// Thanks to https://stackoverflow.com/questions/6632399/given-an-email-as-raw-text-how-can-i-send-it-using-php
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
if (!$smtp_connection) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
@@ -485,7 +489,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
for ($i=0; $i < count($postfix_talk); $i++) {
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
if (substr($smtp_resource, 0, 3) !== $postfix_talk[$i][0]) {
|
||||
$ret = substr($smtp_resource, 0, 3);
|
||||
$ret = (empty($ret)) ? '-' : $ret;
|
||||
@@ -833,7 +837,7 @@ function quarantine($_action, $_data = null) {
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `qhash` = :hash');
|
||||
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `qhash` = :hash');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
break;
|
||||
|
||||
@@ -1089,6 +1089,8 @@ jQuery(function($){
|
||||
return str;
|
||||
}).join('<br>\n');
|
||||
item.subject = escapeHtml(item.subject);
|
||||
if (item.sender_mime != null) item.sender_mime = escapeHtml(item.sender_mime);
|
||||
if (item['message-id'] != null) item['message-id'] = escapeHtml(item['message-id']);
|
||||
var scan_time = item.time_real.toFixed(3);
|
||||
if (item.time_virtual) {
|
||||
scan_time += ' / ' + item.time_virtual.toFixed(3);
|
||||
@@ -1212,6 +1214,11 @@ jQuery(function($){
|
||||
});
|
||||
} else if (table == 'rllog') {
|
||||
$.each(data, function (i, item) {
|
||||
if (item.header_from != null) item.header_from = escapeHtml(item.header_from);
|
||||
if (item.header_subject != null) item.header_subject = escapeHtml(item.header_subject);
|
||||
if (item.from != null) item.from = escapeHtml(item.from);
|
||||
if (item.rcpt != null) item.rcpt = escapeHtml(item.rcpt);
|
||||
if (item.message_id != null) item.message_id = escapeHtml(item.message_id);
|
||||
if (item.user == null) {
|
||||
item.user = "none";
|
||||
}
|
||||
|
||||
@@ -996,6 +996,7 @@ jQuery(function($){
|
||||
'style="min-width:2em;width:' + item.percent_in_use + '%">' + item.percent_in_use + '%' + '</div></div>'
|
||||
};
|
||||
item.username = escapeHtml(item.username);
|
||||
item.name = escapeHtml(item.name);
|
||||
|
||||
if (Array.isArray(item.tags)){
|
||||
var tags = '';
|
||||
|
||||
+11
-2
@@ -8,8 +8,12 @@ $ALLOW_ADMIN_EMAIL_LOGIN = (preg_match(
|
||||
$session_var_user_allowed = 'sogo-sso-user-allowed';
|
||||
$session_var_pass = 'sogo-sso-pass';
|
||||
|
||||
// only the internal nginx auth_request loopback (127.0.0.1:65510) sets this;
|
||||
// external clients can never supply it (bare fastcgi param, not an HTTP header)
|
||||
$is_internal_auth = (($_SERVER['SOGO_AUTH_INTERNAL'] ?? '') === '1');
|
||||
|
||||
// validate credentials for basic auth requests
|
||||
if (isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
if ($is_internal_auth && isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
// load prerequisites only when required
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
|
||||
@@ -50,6 +54,11 @@ elseif (isset($_GET['login'])) {
|
||||
(($_SESSION['acl']['login_as'] == "1" && $ALLOW_ADMIN_EMAIL_LOGIN !== 0) || ($is_dual === false && $login == $_SESSION['mailcow_cc_username']))) {
|
||||
if (filter_var($login, FILTER_VALIDATE_EMAIL)) {
|
||||
if (user_get_alias_details($login) !== false) {
|
||||
// enforce tenant boundary
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $login)) {
|
||||
header("Location: /");
|
||||
exit;
|
||||
}
|
||||
// Block SOGo access if pending actions (2FA setup, password update)
|
||||
if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) {
|
||||
header("Location: /");
|
||||
@@ -80,7 +89,7 @@ elseif (isset($_GET['login'])) {
|
||||
exit;
|
||||
}
|
||||
// only check for admin-login on sogo GUI requests
|
||||
elseif (isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0) {
|
||||
elseif ($is_internal_auth && isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0) {
|
||||
// this is an nginx auth_request call, we check for existing sogo-sso session variables
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.inc.php';
|
||||
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php')) {
|
||||
|
||||
+1
-1
@@ -419,7 +419,7 @@ services:
|
||||
- php-fpm-mailcow
|
||||
- sogo-mailcow
|
||||
- rspamd-mailcow
|
||||
image: ghcr.io/mailcow/nginx:1.30.4
|
||||
image: ghcr.io/mailcow/nginx:1.30.3-1
|
||||
dns:
|
||||
- ${IPV4_NETWORK:-172.22.1}.254
|
||||
environment:
|
||||
|
||||
Reference in New Issue
Block a user