mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-11-11 02:41:33 +00:00
Merge branch 'staging' into nightly
This commit is contained in:
33 files changed
+355
-145
No files matched your search
@@ -8,7 +8,7 @@ if(file_exists('inc/vars.local.inc.php')) {
|
||||
$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config);
|
||||
|
||||
header('Content-type: application/json');
|
||||
if (strtolower($_GET['Protocol']) == 'activesync' && getenv('SKIP_SOGO') != "y") {
|
||||
if (strtolower($_GET['Protocol']) == 'activesync' && $autodiscover_config['autodiscoverType'] == 'activesync' && getenv('SKIP_SOGO') != "y") {
|
||||
echo '{"Protocol":"ActiveSync","Url":"' . $autodiscover_config['activesync']['url'] . '"}';
|
||||
}
|
||||
elseif (strtolower($_GET['Protocol']) == 'autodiscoverv1') {
|
||||
|
||||
@@ -31,7 +31,7 @@ error_reporting(0);
|
||||
|
||||
$data = trim(file_get_contents("php://input"));
|
||||
|
||||
if (strpos($data, 'autodiscover/outlook/responseschema') !== false) {
|
||||
if (strpos($data, 'autodiscover/outlook/responseschema') !== false && $autodiscover_config['autodiscoverType'] == 'activesync') {
|
||||
$autodiscover_config['autodiscoverType'] = 'imap';
|
||||
if ($autodiscover_config['useEASforOutlook'] == 'yes' &&
|
||||
// Office for macOS does not support EAS
|
||||
|
||||
@@ -15,6 +15,10 @@ table.dataTable>tbody>tr.child ul.dtr-details>li {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.129);
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
/* like the first entry has no top padding, the last one needs no bottom padding */
|
||||
table.dataTable>tbody>tr.child ul.dtr-details>li:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
|
||||
background-color: var(--mc-dtr-hover-bg);
|
||||
@@ -28,7 +32,8 @@ table.dataTable td.dt-control:before {
|
||||
color: var(--mc-dtr-color);
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
line-height: 1.25em;
|
||||
line-height: 1em;
|
||||
margin-top: calc(-0.5em - 1.5px);
|
||||
border-radius: 0px;
|
||||
box-shadow: none;
|
||||
font-size: 14px;
|
||||
@@ -39,11 +44,29 @@ table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before,
|
||||
table.dataTable td.dt-control:before {
|
||||
background-color: var(--mc-dtr-active-bg) !important;
|
||||
}
|
||||
/* The default initial order targets column 0, which is usually the non-orderable expand column */
|
||||
table.dataTable thead > tr > th.sorting_disabled.sorting_asc,
|
||||
table.dataTable thead > tr > th.sorting_disabled.sorting_desc {
|
||||
padding-right: 0.5rem;
|
||||
cursor: auto;
|
||||
}
|
||||
table.dataTable thead > tr > th.sorting_disabled.sorting_asc:before,
|
||||
table.dataTable thead > tr > th.sorting_disabled.sorting_asc:after,
|
||||
table.dataTable thead > tr > th.sorting_disabled.sorting_desc:before,
|
||||
table.dataTable thead > tr > th.sorting_disabled.sorting_desc:after {
|
||||
display: none;
|
||||
}
|
||||
table.dataTable.table-striped>tbody>tr>td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
table.dataTable.table-striped>tbody>tr>td>input[type="checkbox"] {
|
||||
margin-top: 7px;
|
||||
/* centers the 1em checkbox in the 1.5em line, level with the expand control */
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
/* the vertical margin is meant for the tag editor in edit forms, in tables it only adds height */
|
||||
table.dataTable .tag-badge {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
td.dtr-col-lg {
|
||||
min-width: 350px;
|
||||
|
||||
@@ -139,6 +139,13 @@ body {
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
}
|
||||
.badge .bi {
|
||||
font-size: inherit;
|
||||
}
|
||||
/* keeps icons from making a badge taller than one with text only */
|
||||
.badge .bi::before {
|
||||
line-height: 0;
|
||||
}
|
||||
.btn-group-xs > .btn, .btn-xs {
|
||||
padding: .25rem .4rem;
|
||||
font-size: .875rem;
|
||||
@@ -343,6 +350,7 @@ code {
|
||||
}
|
||||
.tag-badge .bi {
|
||||
font-size: 12px;
|
||||
line-height: 0;
|
||||
}
|
||||
.tag-badge.btn-badge:hover {
|
||||
filter: brightness(0.9);
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
line-height: 2rem;
|
||||
margin-top: -15px;
|
||||
margin-top: calc(-1rem - 1.5px);
|
||||
}
|
||||
|
||||
li .dtr-data {
|
||||
|
||||
@@ -601,6 +601,13 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){
|
||||
|
||||
// validate mailcow_password
|
||||
$mailcow_password = $user_res['attributes']['mailcow_password'][0];
|
||||
// Keycloak returns binary LDAP attributes like userPassword Base64 encoded
|
||||
if (substr($mailcow_password, 0, 1) != '{') {
|
||||
$decoded_password = base64_decode($mailcow_password, true);
|
||||
if ($decoded_password !== false && substr($decoded_password, 0, 1) == '{') {
|
||||
$mailcow_password = $decoded_password;
|
||||
}
|
||||
}
|
||||
if (!verify_hash($mailcow_password, $pass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,13 @@ function fwdhost($_action, $_data = null) {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$hosts = array_unique(array_map(function ($host) {
|
||||
$net = explode('/', $host, 2);
|
||||
if (filter_var($net[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$net[0] = inet_ntop(inet_pton($net[0]));
|
||||
}
|
||||
return implode('/', $net);
|
||||
}, $hosts));
|
||||
foreach ($hosts as $host) {
|
||||
try {
|
||||
$redis->hSet('WHITELISTED_FWD_HOST', $host, $source);
|
||||
|
||||
@@ -1399,6 +1399,18 @@ function set_tfa($_data) {
|
||||
case "webauthn":
|
||||
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
||||
|
||||
// Refuse incomplete WebAuthn registrations. Without this guard a request
|
||||
// that does not contain the parsed credential data (e.g. a plain form
|
||||
// POST) would silently insert an empty keyHandle while reporting success.
|
||||
if (empty($_data['registration']) || empty($_data['registration']->credentialId)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_data_log),
|
||||
'msg' => 'webauthn_registration_failed'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
|
||||
VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
|
||||
$stmt->execute(array(
|
||||
@@ -1421,11 +1433,11 @@ function set_tfa($_data) {
|
||||
// Block TFA removal if force_tfa policy is active
|
||||
$is_forced_tfa = false;
|
||||
if ($_SESSION['mailcow_cc_role'] === 'user') {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
} else {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
}
|
||||
@@ -1692,11 +1704,11 @@ function unset_tfa_key($_data) {
|
||||
// Block key removal if force_tfa policy is active
|
||||
$is_forced_tfa = false;
|
||||
if ($_SESSION['mailcow_cc_role'] === 'user') {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
} else {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
}
|
||||
@@ -2425,7 +2437,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
||||
case "use_ssl":
|
||||
case "use_tls":
|
||||
case "login_provisioning":
|
||||
case "ignore_ssl_errors":
|
||||
case "ignore_ssl_error":
|
||||
$settings[$row["key"]] = boolval($row["value"]);
|
||||
break;
|
||||
default:
|
||||
@@ -2449,7 +2461,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
||||
if ($settings["authsource"] == "ldap"){
|
||||
$settings['use_ssl'] = !isset($settings['use_ssl']) ? false : $settings['use_ssl'];
|
||||
$settings['use_tls'] = !isset($settings['use_tls']) ? false : $settings['use_tls'];
|
||||
$settings['ignore_ssl_errors'] = !isset($settings['ignore_ssl_errors']) ? false : $settings['ignore_ssl_errors'];
|
||||
$settings['ignore_ssl_error'] = !isset($settings['ignore_ssl_error']) ? false : $settings['ignore_ssl_error'];
|
||||
}
|
||||
return $settings;
|
||||
break;
|
||||
@@ -3111,7 +3123,17 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
||||
case "get-keycloak-admin-token":
|
||||
// get access_token for service account of mailcow client
|
||||
if ($iam_settings['authsource'] !== 'keycloak') return false;
|
||||
if (isset($iam_settings['access_token'])) {
|
||||
// only introspect a cached access_token that is not (about to be) expired,
|
||||
// introspecting an expired token makes Keycloak log INTROSPECT_TOKEN_ERROR
|
||||
$token_not_expired = false;
|
||||
if (!empty($iam_settings['access_token'])) {
|
||||
$token_parts = explode('.', $iam_settings['access_token']);
|
||||
if (count($token_parts) === 3) {
|
||||
$token_payload = json_decode(base64_decode(strtr($token_parts[1], '-_', '+/')), true);
|
||||
$token_not_expired = is_array($token_payload) && isset($token_payload['exp']) && intval($token_payload['exp']) > time() + 30;
|
||||
}
|
||||
}
|
||||
if ($token_not_expired) {
|
||||
// check if access_token is valid
|
||||
$url = "{$iam_settings['server_url']}/realms/{$iam_settings['realm']}/protocol/openid-connect/token/introspect";
|
||||
$req = http_build_query(array(
|
||||
@@ -3162,6 +3184,8 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
||||
':key' => 'access_token',
|
||||
':value' => $res['access_token']
|
||||
));
|
||||
// keep the cached token in sync, so subsequent calls in the same run reuse it
|
||||
$iam_settings['access_token'] = $res['access_token'];
|
||||
return $res['access_token'];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2535,6 +2535,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':domain' => $domain,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `modified` = NOW() WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain,
|
||||
));
|
||||
}
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
@@ -2706,6 +2710,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':domain' => $domain,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `modified` = NOW() WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain,
|
||||
));
|
||||
}
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
@@ -3188,6 +3196,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':username' => $username,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `mailbox` SET `modified` = NOW() WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
@@ -4011,6 +4023,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(':goto' => '(^|,)'.preg_quote($_data, '/').'($|,)'));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
if (!hasAliasObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['address'])) {
|
||||
continue;
|
||||
}
|
||||
// Keep old array for backward compatibility
|
||||
$data['fixed_sender_aliases'][] = $row['address'];
|
||||
// Split into allowed/blocked for proper display
|
||||
@@ -4964,6 +4979,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
else if ($SaslLogs['service'] == 'SSO') {
|
||||
$last_sso_login = strtotime($SaslLogs['datetime']);
|
||||
}
|
||||
else if ($SaslLogs['service'] == 'SOGO') {
|
||||
$last_sogo_login = strtotime($SaslLogs['datetime']);
|
||||
}
|
||||
}
|
||||
if (!isset($last_imap_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||
$last_imap_login = 0;
|
||||
@@ -4977,10 +4995,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
if (!isset($last_sso_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||
$last_sso_login = 0;
|
||||
}
|
||||
if (!isset($last_sogo_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||
$last_sogo_login = 0;
|
||||
}
|
||||
$mailboxdata['last_imap_login'] = $last_imap_login;
|
||||
$mailboxdata['last_smtp_login'] = $last_smtp_login;
|
||||
$mailboxdata['last_pop3_login'] = $last_pop3_login;
|
||||
$mailboxdata['last_sso_login'] = $last_sso_login;
|
||||
$mailboxdata['last_sogo_login'] = $last_sogo_login;
|
||||
|
||||
if (!isset($_extra) || $_extra != 'reduced') {
|
||||
$rl = ratelimit('get', 'mailbox', $_data);
|
||||
@@ -5018,7 +5040,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$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'];
|
||||
$send_as = $sender_acl_row['send_as'];
|
||||
if ($_SESSION['mailcow_cc_role'] == 'domainadmin' && $send_as != '*') {
|
||||
$in_scope = (substr($send_as, 0, 1) == '@')
|
||||
? hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], substr($send_as, 1))
|
||||
: hasAliasObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $send_as);
|
||||
if (!$in_scope) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$mailboxdata['sender_acl'][] = $send_as;
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `tag_name`
|
||||
@@ -5958,6 +5989,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':domain' => $domain,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
if ($stmt->rowCount() > 0) {
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `modified` = NOW() WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6010,6 +6047,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':username' => $username,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
if ($stmt->rowCount() > 0) {
|
||||
$stmt = $pdo->prepare("UPDATE `mailbox` SET `modified` = NOW() WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -616,7 +616,7 @@ jQuery(function($){
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: 'RL',
|
||||
title: lang_edit.ratelimit,
|
||||
data: 'rl',
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
@@ -717,10 +717,6 @@ jQuery(function($){
|
||||
$.each(json, function (i, item) {
|
||||
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="domain_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
|
||||
|
||||
item.attributes.def_quota_for_mbox = humanFileSize(item.attributes.def_quota_for_mbox);
|
||||
item.attributes.max_quota_for_mbox = humanFileSize(item.attributes.max_quota_for_mbox);
|
||||
item.attributes.max_quota_for_domain = humanFileSize(item.attributes.max_quota_for_domain);
|
||||
|
||||
item.template = escapeHtml(item.template);
|
||||
if (item.attributes.rl_frame === "s"){
|
||||
item.attributes.rl_frame = lang_rl.second;
|
||||
@@ -803,16 +799,25 @@ jQuery(function($){
|
||||
title: lang.mailbox_defquota,
|
||||
data: 'attributes.def_quota_for_mbox',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.max_quota,
|
||||
data: 'attributes.max_quota_for_mbox',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.domain_quota_total,
|
||||
data: 'attributes.max_quota_for_domain',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.gal,
|
||||
@@ -934,6 +939,7 @@ jQuery(function($){
|
||||
item.last_mail_login = (item.attributes.imap_access == 1 ? '<div class="text-start badge bg-info mb-2" style="min-width: 70px;">IMAP @ ' + unix_time_format(Number(item.last_imap_login)) + '</div><br>' : '') +
|
||||
(item.attributes.pop3_access == 1 ? '<div class="text-start badge bg-info mb-2" style="min-width: 70px;">POP3 @ ' + unix_time_format(Number(item.last_pop3_login)) + '</div><br>' : '') +
|
||||
(item.attributes.smtp_access == 1 ? '<div class="text-start badge bg-info mb-2" style="min-width: 70px;">SMTP @ ' + unix_time_format(Number(item.last_smtp_login)) + '</div><br>' : '') +
|
||||
'<div class="text-start badge bg-info mb-2" style="min-width: 70px;">SOGO @ ' + unix_time_format(Number(item.last_sogo_login)) + '</div><br>' +
|
||||
'<div class="text-start badge bg-info" style="min-width: 70px;">SSO @ ' + unix_time_format(Number(item.last_sso_login)) + '</div>';
|
||||
/*
|
||||
if (!item.rl) {
|
||||
@@ -1258,8 +1264,6 @@ jQuery(function($){
|
||||
item.attributes.rl_value = (!item.attributes.rl_value) ? "∞" : escapeHtml(item.attributes.rl_value);
|
||||
item.attributes.ratelimit = item.attributes.rl_value + " " + item.attributes.rl_frame;
|
||||
|
||||
item.attributes.quota = humanFileSize(item.attributes.quota);
|
||||
|
||||
item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_in == 1 ? '1' : '0') + '</span></i>';
|
||||
item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_out == 1 ? '1' : '0') + '</span></i>';
|
||||
item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.pop3_access == 1 ? '1' : '0') + '</span></i>';
|
||||
@@ -1346,6 +1350,9 @@ jQuery(function($){
|
||||
title: lang.domain_quota,
|
||||
data: 'attributes.quota',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.tls_enforce_in,
|
||||
|
||||
@@ -1194,10 +1194,14 @@ if (isset($_GET['query'])) {
|
||||
$data['mailboxes'][$mailbox][] = $mailbox;
|
||||
$user_alias_details = user_get_alias_details($mailbox);
|
||||
foreach ($user_alias_details['direct_aliases'] as $k => $v) {
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], substr(strrchr($k, '@'), 1))) {
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
}
|
||||
}
|
||||
foreach ($user_alias_details['shared_aliases'] as $k => $v) {
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], substr(strrchr($k, '@'), 1))) {
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1642,7 +1642,7 @@
|
||||
"mailbox_defaults_info": "Define default settings for new mailboxes.",
|
||||
"mailbox_defquota": "Default mailbox size",
|
||||
"mailbox_templates": "Mailbox Templates",
|
||||
"mailbox_quota": "Max. size of a mailbox",
|
||||
"mailbox_quota": "Max. mailbox size",
|
||||
"mailboxes": "Mailboxes",
|
||||
"max_aliases": "Max. aliases",
|
||||
"max_mailboxes": "Max. possible mailboxes",
|
||||
@@ -2018,6 +2018,7 @@
|
||||
"u2f_deprecated": "It seems that your Key was registered using the deprecated U2F method. We will deactivate Two-Factor-Authenticaiton for you and delete your Key.",
|
||||
"u2f_deprecated_important": "Please register your Key in the admin panel with the new WebAuthn method.",
|
||||
"webauthn": "WebAuthn authentication",
|
||||
"webauthn_registration_failed": "WebAuthn registration failed. The credential could not be verified, please try again.",
|
||||
"waiting_usb_auth": "<i>Waiting for USB device...</i><br><br>Please tap the button on your USB device now.",
|
||||
"waiting_usb_register": "<i>Waiting for USB device...</i><br><br>Please enter your password above and confirm your registration by tapping the button on your USB device.",
|
||||
"yubi_otp": "Yubico OTP authentication"
|
||||
|
||||
@@ -1074,7 +1074,6 @@
|
||||
"verified_webauthn_login": "Inicio de sesión WebAuthn verificado",
|
||||
"verified_yotp_login": "Inicio de sesión Yubico OTP verificado",
|
||||
"admin_added": "Se agregó el administrador %s",
|
||||
"admin_api_modified": "Se guardaron los cambios en la API",
|
||||
"admin_removed": "Se eliminó el administrador %s",
|
||||
"app_links": "Se guardaron los cambios en los enlaces de aplicaciones",
|
||||
"app_passwd_added": "Se agregó una nueva contraseña de aplicación",
|
||||
@@ -1333,10 +1332,6 @@
|
||||
"sogo_profile_reset_help": "Esto destruirá el perfil de SOGo del usuario y <b>eliminará de forma irrecuperable todos sus contactos y datos de calendario</b>.",
|
||||
"spam_score_reset": "Restablecer al valor predeterminado del servidor",
|
||||
"spamfilter_table_domain_policy": "n/d (política del dominio)",
|
||||
"syncjob_check_log": "Revisar registro",
|
||||
"syncjob_EX_OK": "Correcto",
|
||||
"syncjob_EXIT_OVERQUOTA": "El buzón de destino superó su cuota",
|
||||
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Usuario o contraseña incorrectos",
|
||||
"tfa_info": "La autenticación de dos factores ayuda a proteger su cuenta. Si la activa, necesitará contraseñas de aplicación para iniciar sesión en aplicaciones o servicios que no admiten la autenticación de dos factores (p. ej., clientes de correo)."
|
||||
},
|
||||
"warning": {
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
"bcc_dest_format": "Il destinatario in copia nascosta deve essere un singolo indirizzo email.<br>Se si vuole spedire una copia del messaggio a più destinatari, bisogna creare un alias ed utilizzarlo per questa opzione.",
|
||||
"app_passwd_protocols": "Protocolli consentiti per la password dell'app",
|
||||
"tags": "Tag",
|
||||
"dry": "Simula sincronizzazione",
|
||||
"internal": "Interno",
|
||||
"internal_info": "Gli alias interni sono accessibili solo dal proprio dominio o dai domini alias.",
|
||||
"sender_allowed": "Consenti l'invio con questo alias"
|
||||
|
||||
@@ -418,18 +418,24 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
||||
return navigator.credentials.create(json);
|
||||
}).then(function(cred) {
|
||||
return {
|
||||
id: cred.id,
|
||||
rawId: arrayBufferToBase64(cred.rawId),
|
||||
response: {
|
||||
attestationObject: arrayBufferToBase64(cred.response.attestationObject),
|
||||
clientDataJSON: arrayBufferToBase64(cred.response.clientDataJSON)
|
||||
},
|
||||
type: cred.type
|
||||
clientDataJSON: arrayBufferToBase64(cred.response.clientDataJSON),
|
||||
attestationObject: arrayBufferToBase64(cred.response.attestationObject),
|
||||
key_id: keyId,
|
||||
tfa_method: 'webauthn',
|
||||
confirm_password: ''
|
||||
};
|
||||
}).then(function(credData) {
|
||||
$('#setup_webauthn_register_data').val(JSON.stringify(credData));
|
||||
$('#setup_webauthn_reg_form input[name=set_tfa]').val('1');
|
||||
$('#setup_webauthn_reg_form').submit();
|
||||
}).then(JSON.stringify).then(function(credentialData) {
|
||||
// send the attestation to the JSON API, like the regular WebAuthn modal does,
|
||||
// so that the credential is parsed and stored correctly
|
||||
return window.fetch('/api/v1/add/webauthn-tfa-registration', {method: 'POST', body: credentialData, cache: 'no-cache'});
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(json) {
|
||||
if (json.success) {
|
||||
window.location = window.location.href.split('#')[0];
|
||||
} else {
|
||||
throw new Error(json.msg);
|
||||
}
|
||||
}).catch(function(err) {
|
||||
$('#setup_webauthn_return_code').show().text(err.message || 'Registration failed');
|
||||
});
|
||||
|
||||
@@ -97,9 +97,11 @@
|
||||
<div class="col-sm-10">
|
||||
<select data-live-search="true" data-width="100%" style="width:100%" id="editSelectSenderACL" name="sender_acl" size="10" multiple>
|
||||
{% for domain in sender_acl_handles.sender_acl_domains.ro %}
|
||||
<option data-subtext="Admin" value="{{ domain }}" disabled selected>
|
||||
{{ lang.edit.dont_check_sender_acl|format(domain) }}
|
||||
</option>
|
||||
{% if domain == '*' or mailcow_cc_role == 'admin' %}
|
||||
<option data-subtext="Admin" value="{{ domain }}" disabled selected>
|
||||
{{ lang.edit.dont_check_sender_acl|format(domain) }}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for domain in sender_acl_handles.sender_acl_domains.rw %}
|
||||
<option value="{{ domain }}" selected>
|
||||
|
||||
Reference in new issue
Block a user